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

发布于 2024-07-30 18:10:59 字数 1156 浏览 9 评论 0 原文

是否存在特定的设计模式来描述提供非抽象默认实现的场景,该默认实现使用空的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()
    }
}

我已经看到这种模式使用了很多次,包括 SAX 框架中的 Java DefaultHandlerMouseAdapter。 在某些情况下,此类类被命名为适配器,但我的印象是适配器模式在两个不同的接口之间进行转换。

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

此外,鉴于某些方法,我不太明白这是如何遵循 NullObject 模式可以有一个实现,并且 NullObject 传统上是一个单例。

Is there a specific design pattern that describes the scenario where a non-abstract default implementation is provided that implements all or some of the methods on the interface with empty, NO-OP implementations. This being done with the intent of alleviating subclasses with the burden of implementing methods that they themselves may not need/use:

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

I have seen this pattern used a number of times including Java's DefaultHandler in the SAX framework, and MouseAdapter. In somes cases such classes are named as Adaptors, but I was under the impression that the adapter pattern translates between two different interfaces.

Given that in these instances there is only one declared interface that is being translated to an undefined subset of that interface - I am not clear on how this is in the spirit of the adapter pattern.

Furthermore, I don't quite see how this adheres to the NullObject pattern either, given that some methods could have an implementation, and the NullObject is traditionally a singleton.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

无法回应 2024-08-06 18:11:00

您应该遵循不同的设计原则:接口隔离原则

接口隔离原则指出,不应强迫客户端实现他们不使用的接口。 与一个胖接口不同,许多基于方法组的小接口是首选,每个方法服务一个子模块。

您不应该实现 MORE 并且您不应该实现 LESS

查看相关的 SE 问题以了解更多详细信息。

接口隔离原则

接口隔离原则-编程到接口

You should follow different design principle : interface-segregation principle

The Interface Segregation Principle states that clients should not be forced to implement interfaces they don't use. Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one sub module.

You should not implement MORE And you should not implement LESS

Have a look at related SE questions for more details.

The Interface Segregation Principle

Interface Segregation Principle- Program to an interface

私野 2024-08-06 18:11:00

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

然而,它听起来也确实像空对象模式。 然而,我觉得它在适配器世界中表现得更好,纯粹是因为它改变了接口的行为,只允许你实现你想要的部分......但它是一个艰难的过程。

我确定这个问题以前有人问过吗?

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

I have seen this design used in spring where they have a class named FlowExecutionListenerAdapter which saves you implementing all the FlowExecutionListener operations.

However, it does sound like the Null Object Pattern too. However I feel it sits better in the Adapter world purely because it changing the behavour of the interface by allowing you only to implement the bit you want...but its a tough one.

I'm sure this question has been asked before?

This sounds similar no? might be worth a read.

飞烟轻若梦 2024-08-06 18:11:00

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

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

It's also used in Swing (WindowAdapter, which implements WindowListener). It's only a convenience adapter, you only have to define 1-2 methods in this way to have a useful windowlistener. This is indeed an instance of the Adapter pattern, also shows the power of the abstract classes. It's even an example to illustrate why multiple implementation inheritance is useful sometimes.

As for the regular Design Patterns, in the Temlate Method you can define hook operations, which may be overriden (unlike abstract methods, which must be), but the default behaviour (usually the NO-OP) is meaningful too.

站稳脚跟 2024-08-06 18:11:00

很好的问题。

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

当我有一个第三方 API 来为复杂操作期间的仪器提供“钩子”时,我可能会编写这种风格的类。 考虑库提供的以下两个类:

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

Great question.

I have started using NoOp as a class name prefix for this pattern. It's short, clear, and not overloaded (like Empty [contains nothing?], Null [Null Object pattern, which is different?], Abstract [Does it provide some implementation?], or Base [Does it provide some implementation?]).

I may write this style of class when I have a third-party API which provides "Hooks" for isntrumentation during a complex operation. Consider the following two classes provided by a library:

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

In this case, you might right a class using this pattern like this:

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() {}
}
心不设防 2024-08-06 18:11:00

这种模式在旧版本的 Java 中很流行。 它是 Java 7 的默认替代方案接口中的方法

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

我同意之前的回答,指出了接口隔离原则。 对骨架实现的需求可能是一种代码味道,表明接口太“胖”并且可能试图做不止一项工作。 在这种情况下,拆分接口比使用虚拟或 noop 逻辑创建骨架实现更好。

This pattern was prevalent in older versions of Java. It is the Java 7 alternative to default methods in interfaces.

Josh Bloch calls it a skeletal implementation. While a skeletal implementation is typically abstract, you needn't force clients to create a subclass if the skeleton itself is sufficient.

I agree with the previous answer pointing out the Interface Segregation Principle. The need for a skeletal implementation can be a code smell indicating an interface is too "fat" and may be trying to do more than one job. Splitting up the interface is preferable in this scenario to creating a skeletal implementation with dummy or noop logic.

暖心男生 2024-08-06 18:11:00

对我来说,这似乎最接近 特殊情况空对象模式。

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

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

To me this seems closest to the Special Case or Null Object pattern.

Your updates suggest something similar to Template Method expect that you don't have a single method that calls each template method e.g.

public void doEverything()
{
  doThis();
  doThat();
  done();
}
鸠书 2024-08-06 18:11:00

您是否在询问空对象模式

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

Are you asking about the Null Object Pattern?

Further to your edit, the MyClass object is nothing more than a default implemenation. I don't think there's any particular design pattern that describes it.

眼眸 2024-08-06 18:11:00

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

多态的本质是,不问对象做什么
输入它是,然后根据答案调用一些行为,你
只需调用该行为即可。 该对象根据其类型执行以下操作:
正确的事。 执行此操作不太直观的地方之一是您
字段中有空值。

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

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

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

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

I believe Martin Fowler would call this a null object pattern. In his Refactoring book[1], Martin introduces null objects as such:

The essence of polymorphism is that instead of asking an object what
type it is and then invoking some behavior based on the answer, you
just invoke the behavior. The object, depending on its type, does the
right thing. One of the less intuitive places to do this is where you
have a null value in a field.

He later adds, "You benefit when many clients want to do the same thing; they can simply rely on the default null behavior." He also introduces an isNull() method for clients requiring variant behaviors.

I would agree that I sometimes see a (often abstract) implementation called an adapter. For example, in the Android framework, AnimatorListenerAdapter (source code here) is described as:

This adapter class provides empty implementations of the methods from Animator.AnimatorListener. Any custom listener that cares only about a subset of the methods of this listener can simply subclass this adapter class instead of implementing the interface directly.

[1] "Refactoring: Improving the Design of Existing Code," Chapter 9, "Simplifying Conditional Expressions," "Introduce Null Object."

半衬遮猫 2024-08-06 18:10:59

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

我通常在类名称后面附加 DoNothing 前缀。 根据其意图,我还使用 BaseDefault (后者被广泛使用)。 也许 MouseAdapter 应该被称为 DefaultMouseListener

在您关心的情况下,您可以使用简单的 DynamicProxy,您必须仅返回一个“好的”默认值(对象为 null,数字为 0 等)。

顺便说一句,这是一个非常好的问题。

编辑

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

There are no design patterns for default implementation.

I usually append DoNothing prefix to the name of class. Depending on it's intent I use also Base or Default (the latter is widely used). Probably MouseAdapter should be called DefaultMouseListener.

In the case you care, you can stub systematically an interface with a simple DynamicProxy, you must return only a "nice" default value (null for Object, 0 for numeric, etc).

BTW this is a very good question.

EDIT

Furthermore this is neither a Stub or a Mock: maybe it can be confused with a Stub but the intent is different.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文