顿搜
观察者模式(Observer)——23种设计模式之行为型模式
在软件构建过程中,我们需要为某些对象建立 一种“通知依赖关系”------一个对象(目标对象) 的状态发生改变,所有的依赖对象(观察者对象) 都将得到通知。如果这样的依赖关系过于紧密, 将使软件不能很好地抵御变化。
观察者定义了对象间一对多的关系,当一个对象的状态变化时,所有依赖它的对象都得到通知并且自动地更新。
观察者模式又叫发布订阅模式,是对象之间多对一依赖的一种实际方案,被依赖的对象为Subject,依赖的对象为Observer。多个观察者同时监听一个主题对象,当主题对象发生变化时,就会通知所有的观察者对象,使得他们能够自动更新。
一、类图表示
观察者模式涉及到Subject(目标),Observer(观察者),ConcreteSubject(具体目标),ConcreteObserver(具体观察者)等角色
- ①、Subject(目标):目标知道它的观察者。可以有任意多个观察者观察同一个目标。提供注册和删除观察者对象的接口。
- ②、Observer(观察者):为那些在目标发生改变时需获得通知的对象定义一个更新接口。
- ③、ConcreteSubject(具体目标):将有关状态存入各ConcreteObserver对象。当它的状态发生改变时, 向它的各个观察者发出通知。
- ④、ConcreteObserver(具体观察者):维护一个指向ConcreteSubject对象的引用。存储有关状态,这些状态应与目标的状态保持一致。实现Observer的更新接口以使自身状态与目标的状态保持一致。
二、代码示例
public interface Subject {
//methods to register and unregister observers
public void register(Observer obj);
public void unregister(Observer obj);
//method to notify observers of change
public void notifyObservers();
//method to get updates from subject
public Object getUpdate(Observer obj);
}public interface Observer {
//method to update the observer, used by subject
public void update();
//attach with subject to observe
public void setSubject(Subject sub);
}public class MyTopic implements Subject {
private List<Observer> observers;
private String message;
private boolean changed;
private final Object MUTEX= new Object();
public MyTopic(){
this.observers=new ArrayList<>();
}
@Override
public void register(Observer obj) {
if(obj == null) throw new NullPointerException("Null Observer");
if(!observers.contains(obj)) observers.add(obj);
}
@Override
public void unregister(Observer obj) {
observers.remove(obj);
}
@Override
public void notifyObservers() {
List<Observer> observersLocal = null;
//synchronization is used to make sure any observer registered after message is received is not notified
synchronized (MUTEX) {
if (!changed)
return;
observersLocal = new ArrayList<>(this.observers);
this.changed=false;
}
for (Observer obj : observersLocal) {
obj.update();
}
}
@Override
public Object getUpdate(Observer obj) {
return this.message;
}
//method to post message to the topic
public void postMessage(String msg){
System.out.println("Message Posted to Topic:"+msg);
this.message=msg;
this.changed=true;
notifyObservers();
}
}public class MyTopicSubscriber implements Observer {
private String name;
private Subject topic;
public MyTopicSubscriber(String nm){
this.name=nm;
}
@Override
public void update() {
String msg = (String) topic.getUpdate(this);
if(msg == null){
System.out.println(name+":: No new message");
}else
System.out.println(name+":: Consuming message::"+msg);
}
@Override
public void setSubject(Subject sub) {
this.topic=sub;
}
}public class ObserverPatternTest {
public static void main(String[] args) {
//create subject
MyTopic topic = new MyTopic();
//create observers
Observer obj1 = new MyTopicSubscriber("Obj1");
Observer obj2 = new MyTopicSubscriber("Obj2");
Observer obj3 = new MyTopicSubscriber("Obj3");
//register observers to the subject
topic.register(obj1);
topic.register(obj2);
topic.register(obj3);
//attach observer to subject
obj1.setSubject(topic);
obj2.setSubject(topic);
obj3.setSubject(topic);
//check if any update is available
obj1.update();
//now send message to subject
topic.postMessage("New Message");
}
}三、模式分析
观察者模式描述了如何建立这种关系
- ①、模式中的关键对象是目标(subject)和观察者(observer)。
- ②、一个目标可有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的观察者都得到通知。
- ③、作为对该通知的响应,每个观察者都将查询目标以使其状态与目标的状态同步。
四、设计原则分析
1、符合OCP原则
使用该模式的动机就是为了在增加新 的观察对象时可以无需更改被观察的对象。即保持被 观察对象封闭。
2、符合LSP原则
MyTopic可以替换Subject , MyTopicSubscriber可以替换Observer。
3、符合DIP原则
Observer是一个抽象类,具体的 MyTopicSubscriber依赖于它, Subject是不应被实例化的类, 其具体方法也依赖于Observer。
五、模式类型
1、拉模型
- ①、优点是实现比较简单,而且Subject 类和Observer 类可以成为库中的标准可重用元素。
- ②、当调用MyTopicSubscriber的update()方法时, MyTopicSubscriber从MyTopic中”拉出”消息并显示它,很方便。
- ③、但如果你正观察一个具有一千个字段的雇员记录,刚好收到一个更新,这时不知道哪个字段发生了变化?
- ④、如果被观察的对象比较简单,那么拉模型就很合 适。
2、推模型
- ①、推模型的Observer模式可以为上述问题提供帮助。
- ②、notify方法和update方法都带有一个参数,该参数是一个 提示。
- ③、如果被观察者对象比较复杂,并且观察者需要一 个提示,那么推模型合适
六、优缺点分析
1、优点
- ①、Subject和Observers的抽象耦合:Subject只知道 它有一系列的Observers,每个符合抽象的 Observer类的简单接口,但是不知道具体类型
- ②、支持广播通信:Subject对象不关心到底有多少对象对自己感兴趣,其唯一责任是通知它的各观察者。
2、缺点
- ①、一个观察者不知道其它观察者的存在,出现意外的更新时,难以捕捉错误
七、应用场景
当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。
- ①、当一个抽象模型有两个方面, 其中一个方面依赖 于另一方面。将这二者封装在独立的对象中以 使它们可以各自独立地改变和复用。
- ②、当一个对象必须通知其它对象,而又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
- ③、有的东西变化或者发生某件事情的时候,可能引发其他一些对象的行为。或者说,有些对象作为观察者在始终盯着某个对象,一旦有事发生就需要立即行动。
八、总结
观察者定义了对象间一对多的关系,当一个对象的状态变化时,所有依赖它的对象都得到通知并且自动地更新。
- ①、这一模式中的关键对象是目标(subject)和观察者 (observer)。一个目标可以有任意数目的依赖它的观察 者。一旦目标的状态发生改变, 所有的观察者都得到 通知。作为对这个通知的响应,每个观察者都将查询 目标以使其状态与目标的状态同步。
- ②、发出改变请求的Observer对象并不立即更新,而是 将其推迟到它从目标得到一个通知之后。Notify不总是由 目标对象调用。它也可被一个观察者或其它对象调用
