Java 秘密握手反模式
我刚刚遇到了一个我以前见过的模式,我想听听大家对它的看法。所讨论的代码涉及如下接口:Java 秘密握手反模式,java,anti-patterns,Java,Anti Patterns,我刚刚遇到了一个我以前见过的模式,我想听听大家对它的看法。所讨论的代码涉及如下接口: public interface MyCrazyAnalyzer { public void setOptions(AnalyzerOptions options); public void setText(String text); public void initialize(); public int getOccurances(String query); } MyCr
public interface MyCrazyAnalyzer {
public void setOptions(AnalyzerOptions options);
public void setText(String text);
public void initialize();
public int getOccurances(String query);
}
MyCrazyAnalyzer crazy = AnalyzerFactory.getAnalyzer();
crazy.setOptions(true);
crazy.initialize();
Map<String, Integer> results = new HashMap<String, Integer>();
for(String item : items) {
crazy.setText(item);
results.put(item, crazy.getOccurances);
}
预期用途如下:
public interface MyCrazyAnalyzer {
public void setOptions(AnalyzerOptions options);
public void setText(String text);
public void initialize();
public int getOccurances(String query);
}
MyCrazyAnalyzer crazy = AnalyzerFactory.getAnalyzer();
crazy.setOptions(true);
crazy.initialize();
Map<String, Integer> results = new HashMap<String, Integer>();
for(String item : items) {
crazy.setText(item);
results.put(item, crazy.getOccurances);
}
MyCrazyAnalyzer疯狂=AnalyzerFactory.getAnalyzer();
疯狂。设置选项(true);
疯狂。初始化();
映射结果=新的HashMap();
用于(字符串项:项){
疯狂.setText(项目);
结果。放置(项目,疯狂。获取发生);
}
这其中有一些原因。setText(…)和getOccurances(…)之所以存在,是因为在对数据进行同样昂贵的分析之后,可能需要执行多个查询,但这可以重构为一个结果类
我认为这很糟糕的原因:实现以接口未明确指示的方式存储状态。我也看到过类似的情况,涉及一个需要调用“prepareResult”,然后调用“getResult”的接口。现在,我可以想到使用这些特性的设计良好的代码。Hadoop Mapper接口扩展了JobConfigurable和Closeable,但我看到了一个很大的区别,因为它是一个使用用户代码实现这些接口的框架,而服务可能有多个实现。我认为任何与包含一个必须调用的“close”方法相关的事情都是合理的,因为没有其他合理的方法可以做到这一点。在某些情况下,比如JDBC,这是一个泄漏的抽象的结果,但在我想到的两段代码中,很明显这是程序员匆忙地向一个意大利面代码类添加一个接口来清理它的结果
我的问题是:
我不确定这是否是一个描述过的反模式,但我完全同意这是一个设计糟糕的界面。它给错误留下了太多的机会,并且至少违反了一个关键原则:使您的API难以误用 除了误用之外,如果多个线程使用同一实例,此API还可能导致难以调试的错误
实际上,他在API设计方面有着出色的表现(36m16s和40m30s),他将此视为设计拙劣API的特征之一。我看不出有什么不好的地方
setText()
准备阶段;之后,您有一个或多个对getOccurances()
的调用。既然setText()
太贵了,我想不出其他方法来实现这一点
getOccurances(text,query)
将以巨大的性能代价修复“秘密握手”。您可以尝试在getOccurances()
中缓存文本,并且只在文本更改时更新内部缓存,但这看起来越来越像是牺牲了某些OO原则。如果一条规则没有意义,那么就不要应用它。软件开发人员拥有大脑是有原因的。是的,这是一种反模式:
我将重构为
选项
——传递到工厂,以及结果
,从analyseText()
方法返回。一个可能的解决方案——使用流畅的更改。这避免了类包含需要按特定顺序调用的方法。它非常像构建器模式,它确保你不读取仍然在填充的中间的对象。老实说,这个“模式”让我想起了ADO.NET中的DATAIERADER。这并不意味着它一定是一个好主意,但它甚至在严肃的API中也被使用。很好的反模式。如果要像这样初始化对象,我们就可以在接口中指定构造函数。我也喜欢“秘密握手”这个术语。至少它明显不好,不像java.util.MessageFormat在单个方法调用期间使用临时内部状态。我称之为组合锁对象。只有当您以完全正确的顺序调用它时,它才起作用。@Tom MessageFormat与许多其他类(尤其是大多数集合)一样,不是线程安全的。的确,必须查阅文档才能获得这些信息是非常烦人的,但是语言中没有办法使线程安全成为接口的一部分。在这方面,其他语言可能更好,但没有一种语言是我可以肯定的。+1因为我想知道工厂也在做什么。。。似乎它只是被用作一个全局资源,另一个禁忌。很好,现在我可以在签入消息中添加“移除顺序耦合”,而不是不那么礼貌的“移除混乱的垃圾”。正如道格拉斯所建议的,可以将此类代码重构为返回结果对象的内容。