回调/命令与EventListener / Observer模式

时间:2012-01-21 06:52:11

标签: design-patterns asynchronous callback observer-pattern command-pattern

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

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来轻松实现,该CompositeListener在侦听器的优先级上具有其自己的语义,以及如何处理向每个侦听器分发事件,例如,为每个侦听器和串行通知生成一个新线程。 (我实际上认为这是一个好主意,因为它是一个很好的关注点分离,并且是对标准观察者/听众模式的改进)。

有关何时应该使用每种产品的想法?

Thxs。

6 个答案:

答案 0 :(得分:30)

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

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

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

  1. 使用回调模式触发操作并异步通知调用者触发的操作已完成。
  2. 使用 event / observer 模式为其他组件(触发操作)提供操作完成时通知的机会。

答案 1 :(得分:23)

这两种模式都很棒,可供选择的模式取决于您要构建的内容以及框架的使用方式。

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

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

然后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。但我建议您使用guava库中的ListenableFuture

答案 2 :(得分:5)

我认为回调模式更好,因为它更简单,这意味着它更可预测,并且由于它自己的变异状态而不太可能有bug。运行中的一个示例是the way GWT handles browser / server communication

您可能希望使用泛型:

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

答案 3 :(得分:2)

让我们以“灯和开关”为例,看看观察者模式和命令模式之间的区别。

观察者模式

  • 开关是主题,灯列表是观察者,其打开/关闭 可以应用操作。
  • 这是一对多的关系。
  • 打开/关闭动作实际上是最简单的形式。
  • 不以最简单的形式为命令提供封装。

命令模式

  • 另一方面,命令模式中的操作ON / OFF变为Command 课。

  • 命令类包含可以在其上执行操作的接收器Lamp。

  • 命令模式通常提供一对一关系,但是可以 可以扩展以提供一对多的功能。

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

答案 4 :(得分:0)

除了

之外,这两种模式几乎没有共同的意图

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

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

干杯!

答案 5 :(得分:0)

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

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

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

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

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