我刚刚遇到过一种我以前见过的模式,并希望得到它的意见。有问题的代码涉及这样的界面:
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);
}
其中一些原因是有道理的。 setText(...)和getOccurances(...)就在那里,因为在对数据进行相同的昂贵分析后,您可能想要做多个查询,但这可以重构为结果类。
为什么我认为这是如此糟糕:实现是以接口未明确指示的方式存储状态。我也看到类似的涉及需要调用“prepareResult”,然后“getResult”的接口。现在,我可以想到使用其中一些功能的精心设计的代码。 Hadoop Mapper接口扩展了JobConfigurable和Closeable,但我看到了一个很大的不同,因为它是一个使用实现这些接口的用户代码的框架,而不是可能有多个实现的服务。我认为任何与包括必须被调用的“关闭”方法有关的事情都是合理的,因为没有任何其他合理的方法来做到这一点。在某些情况下,就像JDBC一样,这是抽象漏洞的结果,但在我想到的两段代码中,很明显是程序员急忙在意大利面条代码类中添加一个接口来清理它的结果。
我的问题是:
如果这足以得到一个名字,我建议界面的“秘密握手”反模式,当界面本身不具有状态时(如集合),强制您以特定顺序调用多个方法
答案 0 :(得分:17)
是的,这是一种反模式:Sequential coupling。
我重构为Options
- 传递给工厂,并Results
从analyseText()
方法返回。
答案 1 :(得分:3)
答案 2 :(得分:3)
我不确定它是否是一种描述的反模式,但我完全同意这是一个设计糟糕的界面。它留下了太多的错误机会,并且违反了至少一个关键原则:使您的API难以滥用。
除了误用之外,如果多个线程使用同一个实例,这个API也会导致难以调试的错误。
Joshua Bloch实际上在API设计上有一个很好的presentation(36m16s和40m30s),他将此作为设计不佳的API的特征之一。
答案 3 :(得分:0)
我在这里看不到任何不好的东西。 setText()
准备舞台;之后,您有一次或多次致电getOccurances()
。由于setText()
非常昂贵,我无法想到其他任何方法。
getOccurances(text, query)
将以巨大的性能成本修复“秘密握手”。你可以尝试在getOccurances()
中缓存文本,只在文本发生变化时更新你的内部缓存,但开始看起来越来越像牺牲一些OO原则。如果规则没有意义,那么不要应用它。软件开发人员有一个大脑是有原因的。
答案 4 :(得分:0)
一种可能的解决方案 - 使用Fluent chaning。这避免了包含需要按特定顺序调用的方法的类。它与构建器模式非常相似,它确保您不会读取仍处于填充状态的对象。