观察者模式
观察者模式也叫作发布-订阅模式,也就是事件监听机制。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
涉及的角色
1.抽象主题角色
抽象主题角色把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
2.具体主题角色
将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
3.抽象观察者角色
为所有具体观察者提供接口
4.具体的观察者角色
存储与主题的状态相关的状态。
观察者模式实例
抽象主题角色
1 public abstract class Subject { 2 3 private List<Observer> observers = new ArrayList<>(); 4 5 public void attch(Observer observer) 6 { 7 observers.add(observer); 8 System.out.println("Attached an observer"); 9 } 10 11 /** 删除观察者对象 */ 12 public void detach(Observer observer) 13 { 14 observers.remove(observer); 15 System.out.println("Detached an observer"); 16 } 17 18 /** 通知所有注册的观察者对象 */ 19 public void notifyObservers(String newState) 20 { 21 for (int i = 0; i < observers.size(); i++) 22 { 23 observers.get(i).update(newState); 24 } 25 } 26 27 }
具体主题
1 public class ConcreteSubject extends Subject{ 2 3 private String state; 4 5 public String getState() 6 { 7 return state; 8 } 9 10 public void change(String newState) 11 { 12 state = newState; 13 System.out.println("主题状态为:" + state); 14 // 状态发生改变时,通知各个观察者 15 this.notifyObservers(state); 16 } 17 18 }
观察者接口
1 public interface Observer { 2 void update(String state); 3 }
具体观察者接口
1 public class ConcreteObserver implements Observer{ 2 3 private String observerStatus; 4 5 public void update(String state) { 6 observerStatus = state; 7 System.out.println("观察者状态变为"+observerStatus); 8 } 9 10 }
1 public static void main(String[] args) { 2 ConcreteSubject subject = new ConcreteSubject(); 3 Observer observer = new ConcreteObserver(); 4 subject.attch(observer); 5 subject.change("new status"); 6 7 }
结果
Attached an observer 主题状态为:new state 状态为:new state
观察者模式的两种模型
1.推模型
主题对象向观察者推送主题的详细信息,不管观察者是否需要。推送的信息通常是主题对象的全部或部分数据。上面例子就是推模型。
2.拉模型
主题对象在通知观察者时,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中去获取,相当于观察者从主题对象中拉数据。
推模型是假设主题对象知道观察者需要的数据,拉模型是主题对象不知道观察者需要什么数据,干脆把自身传递过去,让观察者自己取。
推模型也会使得对象难以复用。
观察者模式应用
jdk直接支持观察者模式,是Observer这个接口
1 public interface Observer { 2 /** 3 * This method is called whenever the observed object is changed. An 4 * application calls an <tt>Observable</tt> object's 5 * <code>notifyObservers</code> method to have all the object's 6 * observers notified of the change. 7 * 8 * @param o the observable object. 9 * @param arg an argument passed to the <code>notifyObservers</code> 10 * method. 11 */ 12 void update(Observable o, Object arg); 13 }
1 public class Observable { 2 private boolean changed = false; 3 private Vector obs; 4 5 /** Construct an Observable with zero Observers. */ 6 7 public Observable() { 8 obs = new Vector(); 9 } 10 11 /** 12 * Adds an observer to the set of observers for this object, provided 13 * that it is not the same as some observer already in the set. 14 * The order in which notifications will be delivered to multiple 15 * observers is not specified. See the class comment. 16 * 17 * @param o an observer to be added. 18 * @throws NullPointerException if the parameter o is null. 19 */ 20 public synchronized void addObserver(Observer o) { 21 if (o == null) 22 throw new NullPointerException(); 23 if (!obs.contains(o)) { 24 obs.addElement(o); 25 } 26 } 27 ... 28 }
这是被观察者的父类,也就是主题对象。这是一个线程安全的类,是基于Vector实现的。主题对象中有这些方法对观察者进行操作:
方 法 | 作 用 |
addObserver(Observer o) | 如果观察者与集合中已有的观察者不同,则向对象的观察者集合中添加此观察者 |
clearChanged()、hasChanged()、setChanged() | 这三个方法算是一对,用来标记此观察者对象(主题对象)是否被改变的状态的 |
countObservers() | 返回观察者对象的数目 |
deleteObserver(Observer o) | 从对象的观察者集合中删除某个观察者 |
deleteObservers() | 清除观察者列表 |
notifyObservers()、notifyObservers(Object arg) | 如果本对象有变化则通知所有等级的观察者,调用update()方法 |
观察者模式的优点及应用
1.去重复代码
2.解耦
新增观察者直接new出来注册到主题对象之后就完事了,删除观察者,主题对象调用方法删除就好了。应用场景:
1.对一个对象状态的更新需要其他对象同步更新。
2.对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。