观察者模式又叫做发布-订阅(Publish/Subscribe)模式。
观察者模式observer:定义了一种一对多的依赖关系,让多个观察者对象同时监听某个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖与抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
观察者模式的动机:
将一个系统分割成一系列相互协作的类有一个很不好的副作用那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
结构图:
Subjec类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个Update()方法,这个方法叫做更新方法。
ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
实例图:
优点:
-
建立一套触发机制。
-
Subject和Observer之间是松偶合的,分别可以各自独立改变。
-
Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
-
遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。观察者和被观察者是抽象耦合的。
缺点:
-
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。提高时间消耗及程序的复杂度
-
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
-
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
-
尽管已经用了依赖倒转原则,但是‘抽象通知者’还是依赖‘抽象观察者’,也就是说,万一没有了抽象观察者这样的接口,通知的功能就完不成了。【“抽象通知者”由于不希望依赖“抽象观察者”】
-
另外就是每个具体观察者,收到通知时,要调用的接口并不一定一致。
适用场景:
当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将者两者封装在独立的对象中使它们各自独立地改变和复用。
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。这其实是依赖倒转原则的最佳体现。
委托:
委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。
委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被依次唤起。可以使得委托对象所搭载的方法并不需要属于同一个类。
这样就使得,本来是在‘通知者’类中的增加和减少的抽象观察者集合以及通知时遍历的抽象观察者都不必要了。转到客户端让委托搭载多个方法,这就解决了本来与抽象观察者耦合的问题。
但委托也是由前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。注意,是先有观察者模式,再有委托事件技术的,它们各有优缺点。
事件委托实例:
“看股票观察者”类和“看NBA观察者”类,去掉了父类“抽象观察类”,所以补上一些代码,并将“更新”方法名改为各自适合的方法名。
现实中就是这样的,方法名本就不一定相同。
“抽象通知者”由于不希望依赖“抽象观察者”,所以“增加”和“减少”的方法也就没有必要了(抽象观察者已经不存在了)
下面就是如何处理‘老板’类和‘前台’类的问题。声明一个委托,名叫“EventHandler(事件处理程序)”,无参数,无返回值。