Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/381.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么嘲笑私人领域是不好的?_Java - Fatal编程技术网

Java 为什么嘲笑私人领域是不好的?

Java 为什么嘲笑私人领域是不好的?,java,Java,假设我有一个类A,它内部依赖于类B,在私有字段中初始化: class SomeSpecialProcessing { private BufferedWriter bufferedWriter; // some other fields public SomeSpecialProcessing() { this.bufferedWriter = new BufferedWriter(new FileWriter("something.log"));

假设我有一个类A,它内部依赖于类B,在私有字段中初始化:

class SomeSpecialProcessing {
    private BufferedWriter bufferedWriter;
    // some other fields

    public SomeSpecialProcessing() {
        this.bufferedWriter = new BufferedWriter(new FileWriter("something.log"));
    }

    public String doSomeProcessing() {
        // some special calculation 
        persistToDisc(somethingImportant);
        // some special processing
        return importantResult;
    }

    private void persistToDisc(String somethingImportant) {
        // this.bufferedWriter.write(str);
    }
}
现在我需要为公共方法doSomeProcessing()创建单元测试,但我不想将任何内容写入光盘,因为这是一个耗时的操作。因此,我必须模拟缓冲写入程序

现在,我有两种方法可以做到:

  • 允许使用setter或constructor将SomeSpecialProcessing分类,以在外部设置bufferedWriter,然后在单元测试中,我将通过mock进入该字段
  • 在单元测试中,我将使用反射模拟缓冲写入程序
  • 现在我更喜欢使用反射,但很多人说这是一种糟糕的做法,并建议将缓冲编写器添加到构造函数或setter中。但是我认为仅仅因为单元测试而暴露类的内部字段是愚蠢的

    如何解决这类问题

    但我认为仅仅暴露类的内部字段是愚蠢的 因为单元测试

    它不仅使您的代码更容易进行单元测试,而且使您的代码总体上更灵活

    目前你有一个非常死板的班级。它可以做一些处理并保存到一个名为
    something.log
    的文件中。它以非常特定的方式(使用
    BufferedWriter
    )写入它,这可能是或可能不是写入特定输出的最有效的方式。基本上,您正在从类的用户手中夺走控制权

    通过将依赖性推广到任何可以在某处编写字符串的东西,您可以增加类的灵活性,并允许其用户自行决定提供什么样的最佳依赖性来满足他们的需求

    class SomeSpecialProcessing {
        private Writer writer;
    
        public SomeSpecialProcessing(Writer writer) {
            this.writer = writer;
        }
    
        public String doSomeProcessing() {
            // some special calculation 
            persist(somethingImportant);
            // some special processing
            return importantResult;
        }
    
        private void persist(String somethingImportant) {
            this.writer.write(str);
        }
    }
    
    您的类已经有两个用例:写入实际日志,而不执行任何操作。在某种程度上,你已经需要这种灵活性了。你想通过使用反射来回避这个问题,声称它同样好


    问题是它不那么好。基于反射的测试将变得更加脆弱。想重构私有字段的名称吗?哦,看,我考砸了。是否想将内部实现更改为不使用
    BufferedWriter
    ?哦,看,我的测试失败了。

    公开您正在写入的文件可能比硬编码要好,而且这会使测试更容易。您不希望构造函数在异常情况下失败。初始化BufferedWriter有一个错误的位置。您可以考虑遵循,这将使它更易于测试。您将
    某些特殊处理
    与特定的
    缓冲写入程序
    文件写入程序
    耦合得太紧了。如果您遵循依赖项反转原则,您的下一个问题可能是,“那么我如何管理所有依赖项?”1。到处都是工厂。2.依赖注入框架。但它确实允许您有效地重用代码。@juras依赖倒置是一个原则。依赖注入框架是一种可以用来遵循这一原则的工具。你可以在没有任何框架的情况下练习依赖倒置。我对你的解决方案没有任何异议,但当我编写类时,我只想要具有尽可能简单的接口的黑盒子——只有用户真正需要的东西。我为什么要找个作家?它只是这个类的内部内容,为自己在文件中保存一些内容。没有人会对为什么会这样感兴趣。使用您的解决方案,我必须向用户解释Writer是什么,以及为什么有可能设置它。所以基本上你的解决方案是“让它更灵活”,但我的问题是“我不想让它灵活”。@MichaelI知道重构的这些问题。但是,假设您将创建高度灵活的类A、B、C,并且它们必须以某种困难的方式连接在一起才能实现某些功能F。现在您希望创建此功能F的接口,因为您的功能的用户对手动将某些A、B、C连接在一起不感兴趣,因此,您将创建类F,在该类中,此功能可以由单个函数调用。你又回到了这个问题上!F对A、B、C有内部依赖关系,其中C是启动自动销毁的类。你将如何模仿C?@Jurass如果你想声明“BufferedWriter完全是某个特殊处理的内部,其他任何东西都不应该关心它,它永远不会改变”,那么很好,继续你当前的实现。但是现在请注意,测试边界也在移动——您的测试应该打开
    something.log
    ,并确保正确的输出写入其中。否则,您将模拟出被测类的一部分。@jornsharpe好吧,这将完全通过该模拟实现!我将测试方法bufferedWriter.write(something)是否已使用预假设参数调用。@Jurass否,因为您已通过完全封装它,将该类作为实现细节的一部分。如果你要解雇所有告诉你原因的人,而不改变任何事情,那么问为什么是不好是没有意义的,不管怎样都要坚持下去。