代码之家  ›  专栏  ›  技术社区  ›  Kevin Peterson

秘密握手反模式

  •  8
  • Kevin Peterson  · 技术社区  · 16 年前

    我刚刚遇到了一个我以前见过的模式,我想对此征求意见。所讨论的代码涉及这样一个接口:

    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(…)和getOccurences(…)存在,因为在对数据进行同样昂贵的分析之后,可能需要执行多个查询,但可以将其重构为结果类。

    为什么我认为这是如此糟糕:实现以一种接口没有明确指示的方式存储状态。我也看到过类似的事情,涉及一个需要调用“PrepareResult”然后调用“GetResult”的接口。现在,我可以想到使用这些特性的设计良好的代码。HadoopMapper接口扩展了JobConfigurable和Closeable,但是我看到了很大的不同,因为它是一个使用用户代码实现这些接口的框架,而不是一个可以有多个实现的服务。我认为任何与包含必须调用的“close”方法相关的事情都是合理的,因为没有任何其他合理的方法可以做到这一点。在某些情况下,如JDBC,这是抽象泄漏的结果,但在我所想的两段代码中,很明显这是由于程序员匆忙向一个意大利面代码类添加一个接口来清理它。

    我的问题是:

    1. 每个人都同意这是一个设计糟糕的界面吗?
    2. 这是描述的反模式吗?
    3. 这种初始化是否曾经属于接口?
    4. 这对我来说是否仅仅是错误的,因为我喜欢功能风格和不变性?

    如果这种情况很常见,需要一个名称,那么我建议对一个接口使用“秘密握手”反模式,该模式强制您在接口本身没有状态时(如集合)以特定顺序调用多个方法。

    5 回复  |  直到 7 年前
        1
  •  17
  •   Douglas Leeder    16 年前

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

    我会重构成 Options -传递到工厂,并且 Results ,从返回 analyseText() 方法。

        2
  •  3
  •   GaryF    16 年前
    1. 我希望看到分析接口通过必要的参数并自己进行构造;否则,它到底在做什么?
    2. 不确定它是否有名称,但看起来应该是:)
    3. 是的,有时在接口中有setter并期望类调用它们是很方便的(并且是正确的抽象级别)。我建议你这么做 要求 关于这一事实的大量文件。
    4. 不一定,不。对不可变的偏好肯定是一件好事,有时基于setter/bean的设计也可能是“正确”的选择,但是您给出的示例太过分了。
        3
  •  3
  •   Ronald Wildenberg    16 年前

    我不确定它是否是一个描述的反模式,但我完全同意这是一个设计糟糕的接口。它给错误留下了太多的机会,并且至少违反了一个关键原则:使您的API很难被滥用。

    除了误用,如果多个线程使用同一个实例,此API还可能导致难以调试的错误。

    Joshua Bloch 实际上有一个优秀的 presentation (36M16S和40M30S)关于API设计,他将其作为设计不良的API的特征之一加以论述。

        4
  •  0
  •   Aaron Digulla    16 年前

    我看不出这里有什么不好的地方。 setText() 准备舞台;之后,您有一个或多个电话 getOccurances() . 自从 设置文本() 太贵了,我想不出别的办法。

    getOccurances(text, query) 将以巨大的性能代价修复“秘密握手”。您可以尝试将文本缓存到 获取事件() 并且只在文本更改时更新您的内部缓存,但这看起来越来越像是对某个OO原则的牺牲。如果规则没有意义,就不要应用它。软件开发人员有头脑是有原因的。

        5
  •  0
  •   Sridhar Sarnobat    7 年前

    一个可能的解决方案-使用流畅的通道。这样可以避免类包含需要按特定顺序调用的方法。这与构建器模式非常相似,它确保您不会读取仍在填充过程中的对象。