观察者模式
什么叫做观察者模式?
观察者模式又称为发布订阅模式(Publish/Subscribe)它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个注意对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让他们能够自动更新自己。
观察者模式的组成
-抽象主题角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类或接口来实现。
-抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
-具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。
-具体观察者角色:该角色实现抽象观察者角色所要求的更新接口。以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。通常用一个子类实现。
模拟应用场景
珠宝商运送一批钻石,有黄金强盗准备抢劫,珠宝商雇佣了私人保镖,警察局也派人护送,于是当运输车上路的时候,强盗保镖警察都要观察运输车一举一动。
步骤:
1、创建抽象观察者角色 Watcher.java
2、创建几个具体观察者角色 PoliceWatcher.java、SecurityWatcher.java、ThiefWatcher.java
3、创建抽象被观察角色 Watched,定义三个方法:addWatcher()、removeWatcher()、notifyWatcher()
4、创建具体被观察者角色 ConcreateWatched.java 实现上面的三个方法
5、创建Client来测试
一、创建抽象观察者角色Watcher
package org.burning.sport.design.pattern.observerpattern.one; /** * @Description: 抽象观察者角色 */ public interface Watcher { public void update(); }
二、创建具体的观察者角色 PoliceWatcher.java、SecurityWatcher.java、ThiefWatcher.java
package org.burning.sport.design.pattern.observerpattern.one; /** * 具体观察者角色 */ public class PoliceWatcher implements Watcher { @Override public void update() { System.out.println("运输车有行动,警察护航"); } }
package org.burning.sport.design.pattern.observerpattern.one; /** * @Description: 具体的观察者 */ public class SecurityWatcher implements Watcher{ @Override public void update() { System.out.println("运输车有行动,我是保安"); } }
package org.burning.sport.design.pattern.observerpattern.one; /** * 具体观察者角色 */ public class ThiefWatcher implements Watcher { @Override public void update() { System.out.println("运输车有行动,强盗准备动手"); } }
三、创建抽象被观察者角色
package org.burning.sport.design.pattern.observerpattern.one; /** * @Description: 抽象被观察者角色 */ public interface Watched { public void addWatcher(Watcher watcher); public void removeWatcher(Watcher watcher); public void notifyWatchers();
四、具体被观察者角色 ConcreateWatched.java
package org.burning.sport.design.pattern.observerpattern.one; import java.util.ArrayList; import java.util.List; /** * @Description: 具体被观察者角色 */ public class ConcreteWatched implements Watched { private List<Watcher> list = new ArrayList<Watcher>(); @Override public void addWatcher(Watcher watcher) { if(watcher == null) { throw new NullPointerException(); } if(!list.contains(watcher)) { list.add(watcher); } } @Override public void removeWatcher(Watcher watcher) { if(watcher == null) { throw new NullPointerException(); } list.remove(watcher); } @Override public void notifyWatchers() { for(Watcher watcher : list) { watcher.update(); } } }
五、Client测试
public class ObserverTestMain { public static void main(String[] args) { Watched car = new ConcreteWatched(); Watcher w1 = new SecurityWatcher(); Watcher w2 = new ThiefWatcher(); Watcher w3 = new PoliceWatcher(); car.addWatcher(w1); car.addWatcher(w2); car.addWatcher(w3); car.notifyWatchers(); System.out.println("---------------------------"); car.removeWatcher(w2); car.notifyWatchers(); }
特性:
我推你拉:例子中没有关于数据和状态的变化通知,只是简单通知到各个观察者,告诉他们被观察者有行动。观察者模式在关于目标角色、观察者角色通信的具体实现中,有两个版本。
1、一种情况便是目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到。这种模式被很形象的称为:拉模式——就是说变化的信息是观察者角色主动从目标角色中“拉”出来的。
2、另一种方法那就是我目标角色“服务一条龙”,通知你发生变化的同时,通过一个参数将变化的细节传递到观察者角色中去。这就是“推模式”——管你要不要,先给你啦。这两种模式的使用,取决于系统设计时的需要。如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适。如果目标角色比较简单,则“拉模式”就很合适啦。
优点:
1、观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。被观察者并不认识任何一个具体观察者,它只知道它们都有一个共同的接口。
由于被观察者和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。如果被观察者和观察者都被扔到一起,那么这个对象必然跨越抽象化和具体化层次。
2、观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知。
缺点:
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
3、如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
4、虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
https://gitee.com/play-happy/base-project
参考: