观察者模式
观察者模式又叫发布-订阅模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
观察者模式的特点:将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不变。二观察者模式的关键对象是主题Subject和观察者Object,一个Subject可以有任意数目的依赖他的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时,并不需要知道谁是他的观察者,也就是说,具体观察者是谁,他根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。
应用场景:
当一个对象的改变需要同时改变其他对象的时候,而且他不知道具体有多少对象有待改变时,应该考虑使用观察者模式。当一个抽象模型由两方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使他们各自独立的改变和复用。
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。(依赖倒转原则的最佳体现)
1 /** 2 * Observer类,抽象观察者,为所有的具体观察者定义一个接口, 3 * 在得到主题通知的时候更新自己,这个接口叫做更新接。抽象观察者一般用一个抽象类或者一个接口实现。 4 * 更新接口通常包含一个update()方法,这个方法叫做更新方法。 5 * 6 * Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。 7 * @author 贤元 8 * 9 */ 10 public abstract class Observer { 11 public abstract void update(); 12 }
1 /** 2 * Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。他把所有对观察者对象的引用保存在一个聚集里, 3 * 每个主题都可以有任何数量的观察者。抽象主体提供一个接口,可以增加和删除观察者对象。 4 * 5 * Subject类,他把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主体提供一个借口,可以增加和删除观察者对象。 6 * @author 贤元 7 * 8 */ 9 public abstract class Subject { 10 11 //用一个list集合存放观察者 12 private List<Observer> observers = new LinkedList<Observer>(); 13 14 //增加观察者 15 public void attach(Observer observer){ 16 observers.add(observer); 17 } 18 19 //移除观察者 20 public void detach(Observer observer){ 21 observers.remove(observer); 22 } 23 24 //通知。 25 public void inform(){ 26 for(Observer o:observers){ 27 o.update(); //通知观察者做出相应变化 28 } 29 } 30 31 }
具体主题:
1 /** 2 * ConcreteSubject类,叫做具体主题或具体通知者,将有关状态存入具体观察者对象; 3 * 在具体主题的内部状态改变时,给所有等级过的观察者发出通知。具体主题角色通常用一个具体子类实现。 4 * 5 * ConcreteSubject类,具体主题,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。 6 * @author 贤元 7 * 8 */ 9 public class ConcreteSubject extends Subject{ 10 //具体被观察者状态 11 private String subjectState; 12 13 14 15 public String getSubjectState() { 16 return subjectState; 17 } 18 19 public void setSubjectState(String subjectState) { 20 this.subjectState = subjectState; 21 } 22 23 24 25 }
具体观察者:
1 /** 2 * ConcreteObserver类,具体观察者,实现抽象观察者橘色所要求的更新接口,以便使本身的状态与主题的状态相协调。 3 * 具体观察者角色可以保存一个只想具体主题对象的引用。具体观察者角色通常用一个具体子类实现 4 * 5 *ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。 6 * @author 贤元 7 * 8 */ 9 public class ConcreateObserver extends Observer{ 10 private String name;//观察者名字 11 private String observerState;//观察者状态 12 private ConcreteSubject subject;//主题。具体观察者(也就是此观察者)所要观察的主题 13 14 //构造方法,为属性subject和name初始化 15 public ConcreateObserver(ConcreteSubject subject,String name){ 16 this.subject = subject; 17 this.name = name; 18 } 19 20 @Override 21 public void update() { 22 observerState = subject.getSubjectState(); 23 System.out.println("观察者"+name+"的更新状态是"+observerState); 24 } 25 26 27 28 public ConcreteSubject getSubject() { 29 return subject; 30 } 31 32 public void setSubject(ConcreteSubject subject) { 33 this.subject = subject; 34 } 35 36 }
测试类:
1 /** 2 * 客户端代码 3 * @author 贤元 4 * 5 */ 6 public class TestClient { 7 public static void main(String[] args) { 8 //创建主题 9 ConcreteSubject s= new ConcreteSubject(); 10 //为主题添加观察者 11 s.attach(new ConcreateObserver(s,"X")); 12 s.attach(new ConcreateObserver(s,"Y")); 13 s.attach(new ConcreateObserver(s,"Z")); 14 //模拟改变主题的状态 15 s.setSubjectState("ABC"); 16 //当主题状态改变时,通知观察该主题的观察者做出相应变化 17 s.inform(); 18 19 } 20 }
运行结果:
观察者X的更新状态是ABC
观察者Y的更新状态是ABC
观察者Z的更新状态是ABC
UML图:
不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练更加容易维护、容易扩展和复用,只有这样才可以真正得到提高
--《来自大话设计模式》