使用空方法的默认实现的设计模式

时间:2009-08-11 10:15:38

标签: design-patterns adapter solid-principles null-object-pattern interface-segregation-principle

是否有特定的设计模式,它描述了提供非抽象默认实现的场景,该实现使用空的NO-OP实现实现接口上的所有或部分方法。这样做的目的是减轻子类的负担,实现他们自己可能不需要/使用的方法:

public interface MyInterface {
    public void doThis();
    public void doThat();
    public void done();
}

public class MyClass implements MyInterface {
    public void doThis() {
        // NO-OP
    }
    public void doThat() {
        // NO-OP
    }
    public void done() {
        // Some standard implementation
    }
}

public class MuSubClass extends MyClass {
    public void doThat() {
        // Subclass only cares about doThat()
    }
}

我看过这种模式多次使用,包括Java's DefaultHandler in the SAX frameworkMouseAdapter。在某些情况下,这些类被命名为Adapters,但我的印象是适配器模式在两个不同的接口之间进行转换。

鉴于在这些实例中只有一个声明的接口被转换为该接口的未定义子集 - 我不清楚它是如何符合适配器模式的精神。

此外,我不太清楚这是如何遵循NullObject pattern的,因为某些方法可以有一个实现,而NullObject传统上是一个单例。

9 个答案:

答案 0 :(得分:4)

默认实施没有设计模式。

我通常会在课程名称附加DoNothing前缀。根据它的意图,我也使用BaseDefault(后者被广泛使用)。可能MouseAdapter应该被称为DefaultMouseListener

在你关心的情况下,你可以系统地存在一个简单DynamicProxy的接口,你必须只返回一个“漂亮”的默认值(对象为null,数字为0等)。

这是一个非常好的问题。

修改

此外,这既不是Stub也不是Mock:也许它可能与Stub混淆但意图不同。

答案 1 :(得分:2)

我在春天看到过这个设计,它有一个名为FlowExecutionListenerAdapter的类,它可以帮助你实现所有的FlowExecutionListener操作。

然而,它听起来也像Null对象模式。但是我觉得它在Adapter世界中的表现更好,纯粹是因为它改变了界面的行为,只允许你实现你想要的那个......但是它很难。

我确定之前已经问过这个问题了?

这听起来similar没有?可能值得一读。

答案 2 :(得分:2)

它也用于Swing(WindowAdapter,它实现了WindowListener)。它只是一个方便的适配器,你只需要用这种方式定义1-2个方法就可以拥有一个有用的windowlistener。这确实是Adapter模式的一个实例,也显示了抽象类的强大功能。这甚至是一个例子来说明为什么多个实现继承有时是有用的。

对于常规设计模式,在Temlate方法中,您可以定义可以覆盖的挂钩操作(与抽象方法不同,必须如此),但默认行为(通常是NO-OP)也是有意义的。 / p>

答案 3 :(得分:2)

您应遵循不同的设计原则:interface-segregation principle

  

接口隔离原则规定不应强制客户端实现不使用的接口。而不是一个胖接口,基于方法组,优选许多小接口,每个小接口服务于一个子模块。

你不应该实施更多而你不应该实施更少

详细了解相关的SE问题。

The Interface Segregation Principle

Interface Segregation Principle- Program to an interface

答案 4 :(得分:1)

很棒的问题。

我已经开始使用NoOp作为此模式的类名前缀。它简短,清晰,没有超载(如Empty [什么都不包含?],Null [空对象模式,哪个不同?],Abstract [它是否提供了一些实现?] ,或Base [它是否提供了一些实施?]。

当我有一个第三方API时,我可能会编写这种类型的类,它在复杂的操作过程中为isntrumentation提供了“Hooks”。考虑一下库提供的以下两个类:

public class LongRunningActionRunner {
    public void runSomethingLong(DecisionListener cdh) {
        // ...
    }
}

public interface DecisionListener {
    public void beforeFooHook();
    public void afterFooHook();
    public void beforeBarHook();
    public void afterBarHook();
    public void beforeBazHook();
    public void afterBazHook();
}

在这种情况下,您可能会使用此模式对类进行如下操作:

public class NoOpDecisionListener implements DecisionListener {
    @Override public Something beforeFooHook() {}
    @Override public Something afterFooHook() {}
    @Override public Something beforeBarHook() {}
    @Override public Something afterBarHook() {}
    @Override public Something beforeBazHook() {}
    @Override public Something afterBazHook() {}
}

答案 5 :(得分:1)

此模式在较旧的Java版本中很普遍。它是Java 7 alternative to default methods in interfaces

Josh Bloch称之为skeletal implementation。虽然骨架实现通常是抽象的,但是如果骨架本身足够,则无需强制客户端创建子类。

我同意前面的答案,指出了接口隔离原则。对基本实现的需求可能是代码气味,表明接口太“胖”,并且可能试图做多个工作。在这种情况下,与创建具有虚拟或非逻辑的骨架实现方案相比,最好拆分接口。

答案 6 :(得分:0)

您是否在询问Null Object Pattern

在编辑之后,MyClass对象只不过是默认实现。我认为没有任何特定的设计模式可以描述它。

答案 7 :(得分:0)

对我而言,这似乎最接近Special CaseNull Object模式。

您的更新提示与Template Method类似的内容,期望您没有一种方法可以调用每种模板方法,例如

public void doEverything()
{
  doThis();
  doThat();
  done();
}

答案 8 :(得分:0)

我相信Martin Fowler会将此称为空对象模式。在他的重构书[1]中,Martin引入了零对象:

  

多态性的本质是,而不是询问对象是什么   键入它然后根据答案调用一些行为,你   只是调用行为。该对象取决于它的类型   正确的事。其中一个不那么直观的地方就是你   在字段中具有空值。

他后来补充说,"当许多客户想要做同样的事情时,你会受益;他们可以简单地依赖于默认的null行为。"他还为需要变体行为的客户端引入了一个isNull()方法。

我同意我有时会看到一个称为适配器的(通常是抽象的)实现。例如,在Android框架中,AnimatorListenerAdapter(源代码here)被描述为:

  

此适配器类提供Animator.AnimatorListener方法的空实现。任何只关心此侦听器的方法子集的自定义侦听器都可以简单地子类化此适配器类,而不是直接实现接口。

[1]"重构:改进现有代码的设计,"第9章,"简化条件表达式," "引入空对象。"