Java 为什么嘲笑私人领域是不好的?
假设我有一个类A,它内部依赖于类B,在私有字段中初始化:Java 为什么嘲笑私人领域是不好的?,java,Java,假设我有一个类A,它内部依赖于类B,在私有字段中初始化: class SomeSpecialProcessing { private BufferedWriter bufferedWriter; // some other fields public SomeSpecialProcessing() { this.bufferedWriter = new BufferedWriter(new FileWriter("something.log"));
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()创建单元测试,但我不想将任何内容写入光盘,因为这是一个耗时的操作。因此,我必须模拟缓冲写入程序
现在,我有两种方法可以做到:
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否,因为您已通过完全封装它,将该类作为实现细节的一部分。如果你要解雇所有告诉你原因的人,而不改变任何事情,那么问为什么是不好是没有意义的,不管怎样都要坚持下去。