06设计模式——观察者模式
前言:以下总结来自龙哥---左潇龙博客。
总结的很到位,附上博客链接:http://www.cnblogs.com/zuoxiaolong/p/pattern7.html
通常意义上如果一个对象状态的改变需要通知很多对这个对象关注的一系列对象,就可以使用观察者模式。
定义:观察者模式(有时又被称为发布-订阅模式、模型-视图模式、源-收听者模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。
上面的定义当中,主要有这样几个意思,首先是有一个目标的物件,通俗点讲就是一个类,它管理了所有依赖于它的观察者物件,或者通俗点说是观察者类,并在它自己状态发生变化时,主动发出通知。
简单点概括成通俗的话来说,就是一个类管理着所有依赖于它的观察者类,并且它状态变化时会主动给这些依赖它的类发出通知。
可以看到,我们的被观察者类Observable只关联了一个Observer的列表,然后在自己状态变化时,使用notifyObservers方法通知这些Observer,具体这些Observer都是什么,被观察者是不关心也不需要知道的。
上面就将观察者和被观察者二者的耦合度降到很低了,而我们具体的观察者是必须要知道自己观察的是谁,所以它依赖于被观察者。
观察者接口
//这个接口是为了提供一个统一的观察者做出相应行为的方法 public interface Observer { void update(Observable o); }
具体的观察者
public class ConcreteObserver1 implements Observer{ public void update(Observable o) { System.out.println("观察者1观察到" + o.getClass().getSimpleName() + "发生变化"); System.out.println("观察者1做出相应"); } }
public class ConcreteObserver2 implements Observer{ public void update(Observable o) { System.out.println("观察者2观察到" + o.getClass().getSimpleName() + "发生变化"); System.out.println("观察者2做出相应"); } }
被观察者,它有一个观察者的列表,并且有一个通知所有观察者的方法,通知的方式就是调用观察者通用的接口行为update方法。
public class Observable { List<Observer> observers = new ArrayList<Observer>(); public void addObserver(Observer o){ observers.add(o); } public void changed(){ System.out.println("我是被观察者,我已经发生变化了"); notifyObservers();//通知观察自己的所有观察者 } public void notifyObservers(){ for (Observer observer : observers) { observer.update(this); } } }
客户端
public class Client { public static void main(String[] args) throws Exception { Observable observable = new Observable(); observable.addObserver(new ConcreteObserver1()); observable.addObserver(new ConcreteObserver2()); observable.changed(); } }
JDK中为了方便开发人员,已经写好了现成的观察者接口和被观察者类
//观察者接口,每一个观察者都必须实现这个接口 public interface Observer { //这个方法是观察者在观察对象产生变化时所做的响应动作,从中传入了观察的对象和一个预留参数 void update(Observable o, Object arg); }
//被观察者类 public class Observable { //这是一个改变标识,来标记该被观察者有没有改变 private boolean changed = false; //持有一个观察者列表 private Vector obs; public Observable() { obs = new Vector(); } //添加观察者,添加时会去重 public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } //删除观察者 public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } //notifyObservers(Object arg)的重载方法 public void notifyObservers() { notifyObservers(null); } //通知所有观察者,被观察者改变了,你可以执行你的update方法了。 public void notifyObservers(Object arg) { //一个临时的数组,用于并发访问被观察者时,留住观察者列表的当前状态,这种处理方式其实也算是一种设计模式,即备忘录模式。 Object[] arrLocal; //注意这个同步块,它表示在获取观察者列表时,该对象是被锁定的 //也就是说,在我获取到观察者列表之前,不允许其他线程改变观察者列表 synchronized (this) { //如果没变化直接返回 if (!changed) return; //这里将当前的观察者列表放入临时数组 arrLocal = obs.toArray(); //将改变标识重新置回未改变 clearChanged(); } //注意这个for循环没有在同步块,此时已经释放了被观察者的锁,其他线程可以改变观察者列表 //但是这并不影响我们当前进行的操作,因为我们已经将观察者列表复制到临时数组 //在通知时我们只通知数组中的观察者,当前删除和添加观察者,都不会影响我们通知的对象 for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } //删除所有观察者 public synchronized void deleteObservers() { obs.removeAllElements(); } //标识被观察者被改变过了 protected synchronized void setChanged() { changed = true; } //标识被观察者没改变 protected synchronized void clearChanged() { changed = false; } //返回被观察者是否改变 public synchronized boolean hasChanged() { return changed; } //返回观察者数量 public synchronized int countObservers() { return obs.size(); } }
首先要搞清楚在读者和作者之间是谁观察谁,很明显,应该是读者观察作者。所以作者是被观察者,读者是观察者,除了这两个类之外,我们还需要额外添加一个管理器帮我们管理下作者的列表便于读者关注,于是一个观察者模式的DEMO就出现了。
读者类
//读者类,要实现观察者接口 public class Reader implements Observer{ private String name; public Reader(String name) { super(); this.name = name; } public String getName() { return name; } //读者可以关注某一位作者,关注则代表把自己加到作者的观察者列表里 public void subscribe(String writerName){ WriterManager.getInstance().getWriter(writerName).addObserver(this); } //读者可以取消关注某一位作者,取消关注则代表把自己从作者的观察者列表里删除 public void unsubscribe(String writerName){ WriterManager.getInstance().getWriter(writerName).deleteObserver(this); } //当关注的作者发表新小说时,会通知读者去看 public void update(Observable o, Object obj) { if (o instanceof Writer) { Writer writer = (Writer) o; System.out.println(name+"知道" + writer.getName() + "发布了新书《" + writer.getLastNovel() + "》,非要去看!"); } } }
作者类
//作者类,要继承自被观察者类 public class Writer extends Observable{ private String name;//作者的名称 private String lastNovel;//记录作者最新发布的小说 public Writer(String name) { super(); this.name = name; WriterManager.getInstance().add(this); } //作者发布新小说了,要通知所有关注自己的读者 public void addNovel(String novel) { System.out.println(name + "发布了新书《" + novel + "》!"); lastNovel = novel; setChanged(); notifyObservers(); } public String getLastNovel() { return lastNovel; } public String getName() { return name; } }
管理器帮我们管理这些作者
import java.util.HashMap; import java.util.Map; //管理器,保持一份独有的作者列表 public class WriterManager{ private Map<String, Writer> writerMap = new HashMap<String, Writer>(); //添加作者 public void add(Writer writer){ writerMap.put(writer.getName(), writer); } //根据作者姓名获取作者 public Writer getWriter(String name){ return writerMap.get(name); } //单例 private WriterManager(){} public static WriterManager getInstance(){ return WriterManagerInstance.instance; } private static class WriterManagerInstance{ private static WriterManager instance = new WriterManager(); } }
客户端调用
//客户端调用 public class Client { public static void main(String[] args) { //假设四个读者,两个作者 Reader r1 = new Reader("谢广坤"); Reader r2 = new Reader("赵四"); Reader r3 = new Reader("七哥"); Reader r4 = new Reader("刘能"); Writer w1 = new Writer("谢大脚"); Writer w2 = new Writer("王小蒙"); //四人关注了谢大脚 r1.subscribe("谢大脚"); r2.subscribe("谢大脚"); r3.subscribe("谢大脚"); r4.subscribe("谢大脚"); //七哥和刘能还关注了王小蒙 r3.subscribe("王小蒙"); r4.subscribe("王小蒙"); //作者发布新书就会通知关注的读者 //谢大脚写了设计模式 w1.addNovel("设计模式"); //王小蒙写了JAVA编程思想 w2.addNovel("JAVA编程思想"); //谢广坤取消关注谢大脚 r1.unsubscribe("谢大脚"); //谢大脚再写书将不会通知谢广坤 w1.addNovel("观察者模式"); } }
我们使用观察者模式的用意是为了作者不再需要关心他发布新书时都要去通知谁,更重要的是他不需要关心他通知的是读者还是其它什么人,他只知道这个人是实现了观察者接口的,即我们的被观察者依赖的只是一个抽象的接口观察者接口,而不关心具体的观察者都有谁都是什么,比如以后要是游客也可以关注作者了,那么只要游客类实现观察者接口,那么一样可以将游客列入到作者的观察者列表中。
另外,我们让读者自己来选择自己关注的对象,这相当于被观察者将维护通知对象的职能转化给了观察者,这样做的好处是由于一个被观察者可能有N多观察者,所以让被观察者自己维护这个列表会很艰难,这就像一个老师被许多学生认识,那么是所有的学生都记住老师的名字简单,还是让老师记住N多学生的名字简单?答案显而易见,让学生们都记住一个老师的名字是最简单的。
另外,观察者模式分离了观察者和被观察者二者的责任,这样让类之间各自维护自己的功能,专注于自己的功能,会提高系统的可维护性和可重用性。