秘密握手反模式

时间:2009-04-27 08:21:30

标签: java anti-patterns

我刚刚遇到过一种我以前见过的模式,并希望得到它的意见。有问题的代码涉及这样的界面:

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(...)和getOc​​curances(...)就在那里,因为在对数据进行相同的昂贵分析后,您可能想要做多个查询,但这可以重构为结果类。

为什么我认为这是如此糟糕:实现是以接口未明确指示的方式存储状态。我也看到类似的涉及需要调用“prepareResult”,然后“getResult”的接口。现在,我可以想到使用其中一些功能的精心设计的代码。 Hadoop Mapper接口扩展了JobConfigurable和Closeable,但我看到了一个很大的不同,因为它是一个使用实现这些接口的用户代码的框架,而不是可能有多个实现的服务。我认为任何与包括必须被调用的“关闭”方法有关的事情都是合理的,因为没有任何其他合理的方法来做到这一点。在某些情况下,就像JDBC一样,这是抽象漏洞的结果,但在我想到的两段代码中,很明显是程序员急忙在意大利面条代码类中添加一个接口来清理它的结果。

我的问题是:

  1. 大家都认为这是一个设计不良的界面吗?
  2. 这是一种描述的反模式吗?
  3. 这种初始化是否属于接口?
  4. 这对我来说只是看起来不对,因为我偏爱功能风格和不变性吗?
  5. 如果这足以得到一个名字,我建议界面的“秘密握手”反模式,当界面本身不具有状态时(如集合),强制您以特定顺序调用多个方法

5 个答案:

答案 0 :(得分:17)

是的,这是一种反模式:Sequential coupling

我重构为Options - 传递给工厂,并ResultsanalyseText()方法返回。

答案 1 :(得分:3)

  1. 我希望看到AnalyzerFactory传递必要的参数并进行构造本身;不然,它究竟在做什么?
  2. 不确定它是否有名称,但似乎应该:)
  3. 是的,偶尔在你的界面中设置setter并期望类调用它们是方便的(以及正确的抽象级别)。我建议这样做需要大量记录这一事实。
  4. 不是,不。对不变性的偏好当然是件好事,而基于setter / bean的设计有时候也是“正确”的选择,但是你给出的例子就是把它放得太远了。

答案 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。这避免了包含需要按特定顺序调用的方法的类。它与构建器模式非常相似,它确保您不会读取仍处于填充状态的对象。