35

我正在尝试设计一个异步框架,并想知道人们认为回调模式与观察者模式的优缺点。

Callback pattern:

//example callback
public interface Callback{
    public void notify(MethodResult result);
}

//example method
public class Worker{
  public void doAsyncWork(Callback callback){
     //do work
     callback.notify(result);
  }
}

//example observer pattern
public interface EventListener{
   public void notify(MethodResult result);

}

public class Worker{
  private EventListener listener;
  public registerEventListener(EventListener listener){
   this.listener=listener;
  }
  public void doAsyncWork(){
     //do work
     listener.notify(result);
  }
}

我正在使用一个似乎同时使用这两种模式的框架。EventListener 模式不是典型的模式,因为它没有侦听器列表。这可以很容易地通过创建一个 CompositeListener 来实现,它在侦听器的优先级上具有自己的语义以及如何处理事件到每个侦听器的分配,例如为每个侦听器与串行通知生成一个新线程。(我实际上认为这是一个好主意,因为它很好地分离了关注点,并且是对标准观察者/听众模式的改进)。

关于什么时候应该使用它们有什么想法吗?

谢谢。

4

6 回答 6

36

命令、回调和观察者模式具有不同的语义:

  • 回调- 通知单个调用者某些操作以某些结果完成
  • 观察者- 通知零到 n 个相关方发生了某些事件(例如完成的操作)
  • 命令- 将操作调用封装在对象中,从而使其可通过线路传输或持久化

在您的示例中,您可以结合回调和观察者模式来实现更大的 API 灵活性:

  1. 使用回调模式触发操作并异步通知调用者触发的操作已完成。
  2. 使用事件/观察者模式让其他一些组件(没有触发操作)有机会在操作完成时得到通知。
于 2012-01-22T08:50:04.120 回答
26

这两种模式都很棒,选择哪一种取决于您要构建什么以及如何使用您的框架。

如果您正在尝试构建某种具有以下典型工作流程的发布-订阅系统:

  • 客户端启动异步任务并忘记它
  • 多个处理程序在任务完成时收到通知

那么Observer图案是您的自然选择。当你在做一个框架时,你还应该考虑使用EventBus模式来实现松散耦合。

如果您只需要一个简单的异步执行并且使用您的框架的典型流程是:

  • 启动异步任务
  • 完成后做某事

或者

  • 启动异步任务
  • 做点什么
  • 等到它完成并做某事

那么你应该选择简单的Callback

但为了实现更可用和更干净的 API,我建议您摆脱Callback抽象并设计您的工作代码以返回某种Future.

public interface Worker<T> {

    Future<T> doAsync();

}

并且Worker可以通过以下方式使用:

Future<Integer> future = worker.doAsync();

// some work here

Integer result = future.get(); // waits till async work is done

Future可能是标准的java Future。但我建议你ListenableFuture从番石榴库中使用。

于 2012-01-21T11:58:11.990 回答
5

我认为回调模式更好,因为它更简单,这意味着它更容易预测,并且由于它自己的变异状态而不太可能出现错误。这方面的一个例子就是 GWT 处理浏览器/服务器通信的方式

您可能想使用泛型:

//example callback
public interface Callback<T> {
    public void notify(T result);
}

//example method
public class Worker{
  public void doAsyncWork(Callback<SomeTypeOrOther> callback){
     //do work
     callback.notify(result);
  }
}
于 2012-01-21T07:14:23.280 回答
2

让我们以 Lamp 和 Switch 的例子来看看观察者和命令模式之间的区别。

观察者模式

  • Switch 是一个主题,而一系列灯是观察者,可以对其应用 ON/OFF 操作。
  • 这是一对多的关系。
  • 动作 ON/OFF 实际上是最简单形式的函数。
  • 不以最简单的形式提供对命令的封装。

命令模式

  • 另一方面,在命令模式中,Actions ON/OFF 变为 Command 类。

  • 命令类包含可以对其应用操作的接收器 Lamp。

  • 命令模式通常提供一对一的关系,但也可以缩放以提供一对多。

  • 命令模式为命令提供了适当的封装。

于 2020-09-23T06:27:13.487 回答
0

这两种模式几乎没有共同的意图,除了,

可观察模式可以通知多个侦听器。另一方面,命令模式最适合您需要单个回调处理程序的情况。

在命令模式中,很容易实现撤消操作。

干杯!

于 2018-04-08T06:46:44.503 回答
0
  • 回调:作为参数传递给函数的可执行代码,当特定事件发生时调用该函数。它可以在不同的编程语言中以不同的形式实现,例如函数指针、匿名函数和监听器/观察器(面向对象范式)。它通常指的是单个可执行代码,但这不是必需的。一个反例:Android SDK(API 级别 29)中的 SurfaceHolder 接口允许使用 addCallback() 方法注册多个回调。
  • 侦听器/观察者:在面向对象的范式中,它是一个对象,其方法作为参数传递给特定事件发生时调用的函数。这是多个回调的一种可能实现。
  • 观察者模式:一种面向对象的软件设计模式,它建议使用观察者/监听器来将观察者类与被观察类解耦。

在您的特定代码中,Callback 和 EventListener 之间的唯一区别是:

  • 您只允许注册一个回调,但允许多个 EventListener
  • 回调注册与异步任务的执行同步,EventListener注册不是。

前者更简单,后者更灵活。如果您正在创建一个可重用的框架,则后者更有意义。

命令设计模式是对对象中执行操作所需的所有信息的封装,它与用于通知事件的机制无关。

于 2019-11-13T16:31:36.270 回答