观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
补充说明一些问题:
观察者模式的关键对象时主题Subject和观察者Observer,一个主题可以有很多的观察者
运用的时候:
当一个对象改变时,需要同时改变其他对象,而且不知道有多少对象需要被改变
一个抽象模型有两个方便,其中一个方面依赖另一个方面,这时需要用观察者模式
观察者模式做的就是解除耦合操作,让耦合的对象依赖于抽象而不是依赖于具体
这里需要注意:Observer为什么是实现一个接口,而不是一个抽象类呢
因为事实上在运用过程中不知道被观察者的情况,他们可能是完全不相关的类型,所以运用接口,可以避免这个问题
还有一些问题:
如果操作的系统控件,比如VS,其中的一些控件都是事先写好了的,那么怎么才能实现Oberver的Update操作呢
也就是说将现有的一些完成的控件,只知道控件的固定接口。如何改变为观察者模式:
这里可以参见《大话设计模式P 139》
这里提出可以运用一个委托 EventHandler
delegate void EventHandler(); //声明一个特殊的类
在具体主题实现的时候:
public event EventHandler Update;
public void Notify()
{
Update();
}
而在客户端,就应该将各个观察者的update对应的行为绑定在事件处理程序Update上
Update += new EventHandler(CloseStock());
Update += new EventHandler(CloseNBA());
一个委托可以搭载多个方法,所有的方法被依次唤起
总结:
当观察者是属于同一个类型的时候,可以定义一个抽象观察类,如果是不属于同一个类,那么可以定义一个接口,只需要实现了对应的接口就可以了。如果观察类的接口之类都已经被封装好了,那么就可以考虑使用事件处理程序,搭载各个方法,进行通知。
观察者模式的结构
一个软件系统里面包含了各种对象,就像一片欣欣向荣的森林充满了各种生物一样。在一片森林中,各种生物彼此依赖和约束,形成一个个生物链。一种生物的状态变化会造成其他一些生物的相应行动,每一个生物都处于别的生物的互动之中。
同样,一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其他的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。
下面以一个简单的示意性实现为例,讨论观察者模式的结构。
观察者模式所涉及的角色有:
● 抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
● 具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
● 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
● 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
源代码
抽象主题角色类
- package com.bankht.Observer;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * @author: 特种兵—AK47
- * @创建时间:2012-6-28 下午09:43:26
- *
- * @类说明 :抽象主题角色类
- */
- public abstract class Subject {
- /**
- * 用来保存注册的观察者对象
- */
- private List<Observer> list = new ArrayList<Observer>();
- /**
- * 注册观察者对象
- *
- * @param observer
- * 观察者对象
- */
- public void attach(Observer observer) {
- list.add(observer);
- System.out.println("Attached an observer");
- }
- /**
- * 删除观察者对象
- *
- * @param observer
- * 观察者对象
- */
- public void detach(Observer observer) {
- list.remove(observer);
- }
- /**
- * 通知所有注册的观察者对象
- */
- public void nodifyObservers(String newState) {
- for (Observer observer : list) {
- observer.update(newState);
- }
- }
- }
具体主题角色类
- package com.bankht.Observer;
- /**
- * @author: 特种兵—AK47
- * @创建时间:2012-6-28 下午09:44:36
- *
- * @类说明 :具体主题角色类
- */
- public class ConcreteSubject extends Subject {
- private String state;
- public String getState() {
- return state;
- }
- public void change(String newState) {
- state = newState;
- System.out.println("主题状态为:" + state);
- // 状态发生改变,通知各个观察者
- this.nodifyObservers(state);
- }
- }
抽象观察者角色类
- package com.bankht.Observer;
- /**
- * @author: 特种兵—AK47
- * @创建时间:2012-6-28 下午09:45:20
- *
- * @类说明 : 抽象观察者角色类
- */
- public interface Observer {
- /**
- * 更新接口
- *
- * @param state
- * 更新的状态
- */
- public void update(String state);
- }
具体观察者角色类
- package com.bankht.Observer;
- /**
- * @author: 特种兵—AK47
- * @创建时间:2012-6-28 下午09:45:58
- *
- * @类说明 :具体观察者角色类
- */
- public class ConcreteObserver implements Observer {
- // 观察者的状态
- private String observerState;
- @Override
- public void update(String state) {
- /**
- * 更新观察者的状态,使其与目标的状态保持一致
- */
- observerState = state;
- System.out.println("状态为:" + observerState);
- }
- }
客户端类
- package com.bankht.Observer;
- /**
- * @author: 特种兵—AK47
- * @创建时间:2012-6-28 下午09:56:04
- *
- * @类说明 :客户端类
- */
- public class Client {
- public static void main(String[] args) {
- // 创建主题对象
- ConcreteSubject subject = new ConcreteSubject();
- // 创建观察者对象
- Observer observer = new ConcreteObserver();
- // 将观察者对象登记到主题对象上
- subject.attach(observer);
- // subject.detach(observer);
- // 改变主题对象的状态
- subject.change("new state");
- }
- }
运行结果如下:
- Attached an observer
- 主题状态为:new state
- 状态为:new state
在运行时,这个客户端首先创建了具体主题类的实例,以及一个观察者对象。然后,它调用主题对象的attach()方法,将这个观察者对象向主题对象登记,也就是将它加入到主题对象的聚集中去。
这时,客户端调用主题的change()方法,改变了主题对象的内部状态。主题对象在状态发生变化时,调用超类的notifyObservers()方法,通知所有登记过的观察者对象。