设计模式6-观察者模式
一、概念
行为型模式
观察者模式是一种发布-订阅的形式,定义对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
观察者模式的两种模型:
- 推:主题对象向观察者推送主题的详细信息,不管观察者是否需要。推送的信息通常是主题对象的全部或部分数据。
- 拉:主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中去获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样观察者在需要获取数据的时候,就可以通过这个引用来获取了。
优点
- 去除重复代码,似的代码清晰、易懂、易扩展;
- 解耦。
实现
- 抽象主题角色把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象;
- 将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知;
- 为所有的具体观察者提供一个接口,在得到主题通知时更新自己;
- 存储与主题的状态相关的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态协调。
二、示例
观察者模式有两部分组成Subject、Observer。Subject对象带有绑定观察者。以下创建Subject以及Observer接口和实现了接口Observer的实体类。
Subject.java
1 /** 2 * 创建主题 3 */ 4 public class Subject { 5 6 private List<Observer> observers 7 = new ArrayList<Observer>(); 8 //定义一个状态 9 private int state; 10 11 public int getState() { 12 return state; 13 } 14 15 public void setState(int state) { 16 System.out.println(String.format("主题发现状态变更,原始状态:%d, 新状态:%d", this.state, state)); 17 this.state = state; 18 notifyAllObservers(); 19 } 20 21 //注册观察者 22 public void attach(Observer observer){ 23 observers.add(observer); 24 } 25 26 public void notifyAllObservers(){ 27 for (Observer observer : observers) { 28 observer.update(state); 29 } 30 } 31 }
Observer.java
1 public interface Observer { 2 void update(int state); 3 }
ConcreteObserver(具体观察者)
1 public class ConcreteObserver implements Observer { 2 //观察者的状态 3 private int observerState; 4 @Override 5 public void update(int state) { 6 //更新观察者的状态 7 observerState = state; 8 System.out.println("状态为:" + observerState); 9 } 10 }
客户端调用代码,一旦主题调用了change方法改变观察者的状态,那么观察者Observer里面的observerState全都改变了
调用
public static void main(String[] args) { /** 创建主题角色 */ Subject subject = new Subject (); /** 创建观察者对象 */ Observer observer = new ConcreteObserver(); /** 将观察者注册到主题对象上 */ subject.attach(observer); /** 改变主题对象的状态 */ subject.setState(1); }
以上演示的是推模型。
两种模型的比较
1、推模型是假设主题对象知道观察者需要的数据,拉模型是假设主题对象不知道观察者需要什么数据,干脆把自身传递过去,让观察者自己按需要取值
2、推模型可能会使得观察者对象难以复用,因为观察者的update()方法是按需要定义的参数,可能无法兼顾到没有考虑到的使用情况,这意味着出现新的情况时,可能要提供新的update()方法
三、观察者模式在Java中的应用及解读
JDK是有直接支持观察者模式的,就是java.util.Observer这个接口:
public interface Observer { /** * This method is called whenever the observed object is changed. An * application calls an <tt>Observable</tt> object's * <code>notifyObservers</code> method to have all the object's * observers notified of the change. * * @param o the observable object. * @param arg an argument passed to the <code>notifyObservers</code> * method. */ void update(Observable o, Object arg); }
这就是观察者的接口,定义的观察者只需要实现这个接口就可以了。update()方法,被观察者对象的状态发生变化时,被观察者的notifyObservers()方法就会调用这个方法:
public class Observable { private boolean changed = false; private Vector<Observer> obs; /** Construct an Observable with zero Observers. */ public Observable() { obs = new Vector<>(); } /** * Adds an observer to the set of observers for this object, provided * that it is not the same as some observer already in the set. * The order in which notifications will be delivered to multiple * observers is not specified. See the class comment. * * @param o an observer to be added. * @throws NullPointerException if the parameter o is null. */ public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } ... }
这是被观察者的父类,也就是主题对象。这是一个线程安全的类,是基于Vector实现的。主题对象中有这些方法对观察者进行操作:
方 法 | 作 用 |
addObserver(Observer o) | 如果观察者与集合中已有的观察者不同,则向对象的观察者集合中添加此观察者 |
clearChanged()、hasChanged()、setChanged() | 这三个方法算是一对,用来标记此观察者对象(主题对象)是否被改变的状态的 |
countObservers() | 返回观察者对象的数目 |
deleteObserver(Observer o) | 从对象的观察者集合中删除某个观察者 |
deleteObservers() | 清除观察者列表 |
notifyObservers()、notifyObservers(Object arg) | 如果本对象有变化则通知所有等级的观察者,调用update()方法 |
利用JDK实现的主题观察者示例:
观察者
public class Watched extends Observable { private String data = ""; public String getData() { return data; } public void setData(String data) { if (!this.data.equals(data)) { this.data = data; setChanged(); } notifyObservers(); } }
主题
public class Watcher implements Observer { String data; public Watcher(Observable o) { o.addObserver(this); } public String getData() { return data; } public void update(Observable o, Object arg) { this.data = ((Watched)o).getData(); System.out.println("状态发生改变:" + ((Watched)o).getData()); } }
调用
public static void main(String[] args) { /** 创建被观察者对象 */ Watched watched = new Watched(); /** 创建观察者对象,并将被观察者对象登记 */ Watcher watcher = new Watcher(watched); /** 给被观察者状态赋值 */ watched.setData("start"); watched.setData("run"); watched.setData("stop"); }
实际应用场景:
- 对一个对象状态的更新需要其他对象同步更新;
- 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节,如消息推送。