回调/命令与EventListener/Observer模式



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

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

你对什么时候应该使用每个有什么想法吗?

Thxs。

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

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

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

  1. 使用回调模式触发操作,并异步通知调用者触发的操作已完成
  2. 使用事件/观察器模式,当操作完成时,为其他一些组件(没有触发操作的组件)提供通知的机会

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

如果你正试图用以下典型的工作流程来构建某种发布订阅系统:

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

那么CCD_ 1模式是您的自然选择。在编写框架时,还应该考虑使用EventBus模式来实现松耦合。

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

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

  • 启动异步任务
  • 做点什么
  • 等到它完成后再做点什么

那么您应该使用简单的CCD_ 2。

但是,为了实现更可用和更干净的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

我认为回调模式更好,因为它更简单,这意味着它将更可预测,并且由于其自身的变化状态而不太可能出现错误。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);
  }
}

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

观察者模式

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

命令模式

  • 另一方面,在命令模式中,Actions On/OFF变为command班

  • 命令类包含接收器指示灯,可以对其应用操作。

  • 命令模式通常提供一对一关系,但是可以可以按比例缩放以提供一对多。

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

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

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

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

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

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

观察者模式和命令模式几乎没有共同的意图,可以组合使用。但不同之处在于他们的动机。

观察者模式描述了如何在主体观察员之间建立关系。它没有把重点放在将命令创建为一个单独的类并对其进行参数化,以便可以存储命令对象并支持可撤消的操作。但这就是Command模式的作用。

命令模式描述了如何在调用程序指令接收方之间建立关系。它没有关注如何支持任何数量的观察员以及如何管理他们。但这就是观察者模式的作用。

最新更新