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

Design patterns 使用空方法的默认实现的设计模式,design-patterns,adapter,solid-principles,null-object-pattern,interface-segregation-principle,Design Patterns,Adapter,Solid Principles,Null Object Pattern,Interface Segregation Principle,是否有一个特定的设计模式来描述这样一种场景,其中提供了一个非抽象的默认实现,该实现使用空的、无操作的实现来实现接口上的所有或部分方法。这样做的目的是减轻子类实现自己可能不需要/使用的方法的负担: public interface MyInterface { public void doThis(); public void doThat(); public void done(); } public class MyClass implements MyInterfac

是否有一个特定的设计模式来描述这样一种场景,其中提供了一个非抽象的默认实现,该实现使用空的、无操作的实现来实现接口上的所有或部分方法。这样做的目的是减轻子类实现自己可能不需要/使用的方法的负担:

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()
    }
}
我已经多次使用此模式,包括和。在某些情况下,这样的类被命名为适配器,但我的印象是适配器模式在两个不同的接口之间转换

考虑到在这些实例中,只有一个已声明的接口被转换为该接口的未定义子集,我不清楚这是如何体现适配器模式的精神的

此外,我也不太明白这是如何遵循的,因为有些方法可能有一个实现,而NullObject传统上是一个单例对象。

你是在问这个问题吗


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

对我来说,这似乎与or模式最接近

您的更新建议类似于您没有一个方法调用每个模板方法,例如

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

我在spring中看到过这种设计,其中有一个名为的类,它可以帮助您实现所有FlowExecutionListener操作

但是,它听起来也像空对象模式。不过,我觉得它在适配器世界中的地位更好,因为它改变了接口的行为,只允许您实现所需的位……但这是一个困难的问题

我肯定这个问题以前有人问过


这听起来不是吗?可能值得一读。

没有默认实现的设计模式。

我通常在类名后面附加
DoNothing
前缀。根据它的用途,我还使用
Base
Default
(后者被广泛使用)。可能应该调用
MouseAdapter
默认MouseListener

在您关心的情况下,您可以使用一个简单的接口,您必须只返回一个“nice”默认值(对象为null,数值为0,等等)

顺便说一句,这是一个很好的问题

编辑

此外,这既不是a也不是a:可能会与存根混淆,但目的不同。

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

至于常规的设计模式,在Temlate方法中,您可以定义钩子操作,钩子操作可以被重写(与抽象方法不同,抽象方法必须被重写),但是默认行为(通常是NO-OP)也很有意义。

好问题

我已经开始使用
NoOp
作为此模式的类名前缀。它简短、清晰且不重载(比如
Empty
[不包含任何内容?]、
Null
[空对象模式,这是不同的?]、
抽象的
[它提供了一些实现吗?]或者
Base
[它提供了一些实现吗?])

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

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() {}
}

我相信Martin Fowler会称之为空对象模式。在他的重构书[1]中,Martin介绍了空对象:

多态性的本质是,不要问对象是什么 键入它,然后根据答案调用某些行为,您可以 只需调用行为。对象根据其类型执行以下操作: 没错。其中一个不那么直观的地方是 字段中有空值

他后来补充道,“当许多客户机想要做同样的事情时,您会受益;他们可以简单地依赖默认的null行为。”他还为需要不同行为的客户机引入了isNull()方法

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

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


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

您应该遵循不同的设计原则:

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

您不应该实施更多,也不应该实施更少

有关更多详细信息,请查看相关SE问题


这种模式在较旧版本的Java中很普遍。这是最重要的

乔希·布洛赫称之为一场战争。虽然骨架实现通常是抽象的,但如果骨架本身足够,则无需强制客户端创建子类

我同意前面指出接口隔离原则的回答。框架实现的必要性