首先声明设计原则:开放-关闭原则,当原代码被修改时,说明设计不够好
依赖倒转原则,让程序依赖抽象,而不是相互
又称为发布-订阅(publish/subscribe)模式.
观察着模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生改变时,会通知所有观察者对象,使他们能够自动更新自己。
适用性:
1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
组成
抽象主题(subject)角色
把所有对观察者的引用保存到一个集合,每一个抽象主题可以有任意数量的观察者。抽象主题提供一个借口,可以增加和删除观察者。一般用接口和抽象类来实现抽象主题。
抽象观察者(observer)角色
为具体观察者提供一个更新(update)接口,在得到主题通知时更新自己。
具体主题(concrete subject)角色
在具体主题内部状态发生改变时,给所有登记过的观察者发通知。是抽象主题的子类。
具体观察者(concrete observer)角色
实现抽象观察者角色所有要求的更新接口,以便本身的状态与主题的状态相协调。如果需要,具体的观察者角色可以保存一个指向具体主题角色的引用,来获得主题角色的状态信息。
结构
代码实现
subject
//在此使用了抽象类,虽然没有办法实现多继承,但是由于不同主题所要执行的功能基本相同,抽象类可以省很多代码量。 public abstract class Subject { public static List<Observer> observers = new ArrayList<Observer>(); public void registerObserver(Observer o) { if(null == o){ throw new NullPointerException(); } if(!observers.contains(o)){ observers.add(o); } } public void removeObserver(Observer o) { if(null == o){ throw new NullPointerException(); } if(observers.contains(o)){ observers.remove(o); } } public void notifyObservers() { for (Observer o:observers) { o.update(); } } }
ConcreteSubject
public class ConcreteSubject extends Subject { public class ConcreteSubject extends Subject { private Object state; public Object getState() { return state; } public void setState(Object state) { this.state = state; } }
observer
// 使用接口,由于具体的观察者模式可以是各种各样的类,所以用接口实现 public interface Observer { public void update(); }
ConcreteObserver
//未修改版,此时使用ConcreteSubject,使用具体实现主题会使得该具体观察者只能接受一个主题对象通知,因为要用this.subject.getState()获得状态,而抽象类Subject没有该属性和方法。 public class ConcreteObserver implements Observer { private ConcreteSubject subject; private Object state; public ConcreteObserver(ConcreteSubject subject){ this.subject = subject; subject.registerObserver(this); } @Override public void update() { state = this.subject.getState(); System.out.println("i am "+this.hashCode()+"and receive "+state); } } /** *修改版一:接收抽象类subject对象,在update方法进行对象类型判断,然后对subject进行转型, *就可以实现getState()方法。 */ public class ConcreteObserver2 implements Observer { private Subject subject; public ConcreteObserver2(Subject subject){ this.subject = subject; subject.registerObserver(this); } @Override public void update() { if(subject instanceof ConcreteSubject){ ConcreteSubject sub = (ConcreteSubject) this.subject; Object state = sub.getState(); System.out.println("i am "+this.hashCode()+"and receive "+state); } } } /** * 修改版本三:具体主题实现notify(object org)推信息, * 而具体观察者实现update(subject sub,object org)拉去信息 */ public class ConcreteObserver3 implements Observer { private Subject3 subject; public ConcreteObserver3(Subject3 subject){ this.subject = subject; subject.registerObserver(this); } @Override public void update() { } @Override public void update(Subject3 subject, Object org) { System.out.println("i am "+this.hashCode()+"and receive "+org); } } //具体实现主题 public class ConcreteSubject3 extends Subject3 { private Object state; public Object getState() { return state; } public void setState(Object state) { this.state = state; } public void notifyObservers(Object arg) { for (Observer o:observers) { o.update(this,arg); } } }
测试
public class Client { public static void main(String[] args) { ConcreteSubject subject = new ConcreteSubject(); Observer observer1 = new ConcreteObserver(subject); Observer observer2 = new ConcreteObserver(subject); Observer observer3 = new ConcreteObserver(subject); //版本二 Observer observer4 = new ConcreteObserver2(subject); Observer observer5 = new ConcreteObserver2(subject); subject.setState("boss is coming!"); subject.notifyObservers(); //版本三 ConcreteSubject3 subject3 = new ConcreteSubject3(); Observer observer6 = new ConcreteObserver3(subject3); Observer observer7= new ConcreteObserver3(subject3); subject3.setState("boss is coming!"); subject3.notifyObservers(subject3.getState()); } }
java对观察者模式的内置支持
java.util.Observable类充当观察者模式中的抽象主题角色(在这里可以将其称为“可观察者”)
java.util.Observer接口充当观察者模式中的抽象观察者角色(体现了“松耦合”)
java内置的对观察者模式的支持结构图为:
不足之处(摘自HeadFirst):java.util.Observable是一个抽象类,就像我们前面所提到的,因为java是单继承的,这使得某类不可能同时具有Observable和其他超类的行为,这限制了Observable的复用潜力(这也是在代码实现1中使用interface的原因)。另外,Observable将关键方法如setChanged()设置成protected,这意味着,:除非你继承Observable类,否则你无法创建Observable实例并组合到你自己的对象中来,这违反了“多用组合,少用继承”的原则。
特点
观察者模式的工作就是在解除耦合,让耦合的双方依赖抽象而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
不足与改进
尽管已经使用了依赖倒转原则,但是抽象通知者还是依赖于抽象观察者,也就是说,一旦没有抽象观察者这样的接口,通知功能就不能实现。并且每一个具体的观察者对象不一定要调用更新方法。
解决方式:观察者与通知者互不相识,建立一个委托者来实现通知业务。
委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,委托可以看作是对函数的抽象,是函数的‘类’,委托实例将代表一个具体的函数。
委托前提:委托对象所搭载的所有方法必须具有相同的原形和形式,也就是说有相同的参数列表和返回值类型。
委托模式在C#中有实现,在java中可以通过动态代理的方式实现
实现原理:
delegate void EvnetHandler();------声明了一个特殊的类 在具体通知者类中实现 public event EventHandler Update; //声明EvnetHandler的委托事件,名称为update-----声明了一个类变量 并在notify()方法中调用Update(),//再通知中调用update() public void notify(){ Update() } ConcreteSubject subject = new ConcreteSubject() ConcreteObserverA a = new ConcreteObserverA() //具体观察者A ConcreteObserverB b = new ConcreteObserverB() //具体观察者B subject.Update += new EventHandler(a.updateXXX()) subject.Update += new EventHandler(b.updateXXX()) ----将a.updateXXX()和b.updateXXX()方法委托给subject.Update subject.SubjectState = "我来了!" subject.notify();
注:一个委托可以搭载多个方法,所有方法会依次被唤起,并且锁搭载的方法可以不属于同一个类。
补充
1,以上的主题或和观察者中的状态(数据/通知的消息)state泛指主题或者观察者中的状态信息,可以是一组数据,并不是只有一个Object类型的状态数据State.
2,根据自己的需要适当实现观察者模式:抽象主题的实现方式(接口或抽象类?)是否在主题或观察者中加入表示本身状态的属性、是否在观察者中加入一个主题类型的引用(这个引用的类型是抽象主题类型的还是具体主题类型的?)、选择传递数据的方式(推或拉?)以及是否采用类似Observable中setChanged()方法适当调整主题通知观察者的程度(是立即通知还是达到一定程度才通知还是..?)
3,以上是简单依赖关系的观察者模式。
对于复杂依赖关系的观察者模式:(摘抄《设计模式》)
封装复杂的更新语义:
当目标和观察者间的依赖关系特别复杂时, 可能需要一个维护这些关系的对象。我们称这样的对象为更改管理器(ChangeManager)。ChangeManager是一个Mediator(中介者)模式的实例。通常只有一个ChangeManager, 并且它是全局可见的。它的目的是尽量减少观察者反映其目标的状态变化所需的工作量。例如, 如果一个操作涉及到对几个相互依赖的目标进行改动, 就必须保证仅在所有的目标都已更改完毕后,才一次性地通知它们的观察者,而不是每个目标都通知观察者。
ChangeManager有三个责任:
a) 它将一个目标映射到它的观察者并提供一个接口来维护这个映射。这就不需要由目标来维护对其观察者的引用, 反之亦然。-----------------接口 Register(Subject,Observer)
b) 它定义一个特定的更新策略。--------当目标更新全部完成时,才会通知它们的观察者
c) 根据一个目标的请求, 它更新所有依赖于这个目标的观察者。
基于ChangeManager的Observer模式的实现
SimpleChangeManager总是更新每一个目标的所有观察者。
DAGChangeManager处理目标及其观察者之间依赖关系构成的无环有向图。
当一个观察者观察多个目标时, ,两个或更多个目标中产生的改变可能会产生冗余的更新。DAGChangeManager保证观察者仅接收一个更新。当不存在多重更新的问题时, SimpleChangeManager更好一些。
参考资料:
1,https://blog.csdn.net/jialinqiang/article/details/8871965 作者:jiajialin
2,《大话数据模式》