设计模式之观察者模式
对于观察者模式而言,肯定有观察者和被观察者之分。比如在一个目录下建立一个文件,这时系统会通知目录管理器增加目录,并通知磁盘减少空间,在这里,文件就是观察者,目录管理器和磁盘就是被观察者。
观察者模式(Observer),区别于发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。观察者模式里,只有两个角色 —— 观察者 + 被观察者,而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个经常被我们忽略的 —— 经纪人Broker;观察者和被观察者,是松耦合的关系,发布者和订阅者是不存在耦合。UML结构图如下:
其中,Subject类是主题,它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供了一个接口,可以增加和删除观察者对象;Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己;ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。
1、主题Subject
首先定义一个观察者数组,并实现增、删及通知操作。它的职责很简单,就是定义谁能观察,谁不能观察,用Vector是线程同步的,比较安全,也可以使用ArrayList,是线程异步的,但不安全。
1 public class Subject { 2 //观察者数组 3 private Vector<Observer> oVector = new Vector<>(); 4 //增加一个观察者 5 public void addObserver(Observer observer) { 6 this.oVector.add(observer); 7 } 8 //删除一个观察者 9 public void deleteObserver(Observer observer) { 10 this.oVector.remove(observer); 11 } 12 //通知所有观察者 13 public void notifyObserver() { 14 for(Observer observer : this.oVector) { 15 observer.update(); 16 } 17 } 18 }
2、抽象观察者Observer
观察者一般是一个接口,每一个实现该接口的实现类都是具体观察者。
1 public interface Observer { 2 //更新 3 public void update(); 4 }
3、具体主题
继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多变种。
1 public class ConcreteSubject extends Subject { 2 //具体业务 3 public void doSomething() { 4 //... 5 super.notifyObserver(); 6 } 7 }
4、具体观察者
1 public class ConcreteObserver implements Observer { 2 @Override 3 public void update() { 4 System.out.println("收到消息,进行处理"); 5 } 6 }
1 public class Client { 2 public static void main(String[] args) { 3 //创建一个主题 4 ConcreteSubject subject = new ConcreteSubject(); 5 //定义一个观察者 6 Observer observer = new ConcreteObserver(); 7 //观察 8 subject.addObserver(observer); 9 //开始活动 10 subject.doSomething(); 11 } 12 }
注意:
(1)一对多,一个对象状态改变,所有依赖对象都将得到通知。
缺点:
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
如果观察者和观察目标间有循环依赖,可能导致系统崩溃
没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的