随着交通信号灯的变化,汽车的行为也将随之变化,一盏交通信号灯可以指挥多辆汽车。
在软件系统中,有些对象之间也存在类似交通信号灯和汽车之间的关系,一个对象状态或者行为的变化将导致其他对象的状态或者行为也发生变化,它们之间将产生联动。为了更好的描述对象之间存在的这种一对多(包括一对一)的联动,观察者模式应运而生,它定义对象之间一对多的依赖关系,让一个对象的改变能够影响其它对象。
观察者模式概述
观察者模式用于建立一种对象之间的依赖关系,一个对象发生改变时将通知其它对象,其它对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有任何相互联系,可以根据需要增加或者删除观察者,使得系统更易于扩展。
观察者模式定义
定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆能得到通知并被自动更新。观察者模式的别名包括“发布--订阅”模式。
观察者模式结构图
观察者模式结构图中包含以下4个角色:
(1) 抽象主题角色:把所有对观察者对象的引用保存在一个集合(std::list)中,每个抽象主题角色都可以有任意数量的观察者。
抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
(2) 具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。
(3) 抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
(4) 具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己。
JDK对观察者模式的支持
观察者模式在java语言中的地位非常重要。在JDK的java.util包中,提供了Obsevable类(观察目标)以及Observer(观察者)接口,它们构成了JDK对观察者模式的支持。
如下图所示:
观察者模式与Java事件处理
Java GUI经典示例
观察者模式与MVC
观察者模式总结
主要优点
(1) 观察者模式可以实现表示层和数据逻辑层的分离。
(2) 观察者模式在观察目标和观察者之间建立了一个抽象的耦合。
(3) 观察者模式支持广播通信,观察目标向所有已注册的观察者对象发送通知,简化一对多系统设计的难度。
主要缺点
(1) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎样发生变化的,而仅仅是知道观察目标发生了变化。
适用场景
(1) 一个抽象模型有两个方面,其中一个方面依赖另一个方面。
(2) 一个对象的改变将导致一个或多个其它对象发生改变,而不知道具体有多少个对象发生改变。
观察者模式应用
简而言之,观察者模式=发布者+注册者。
观察者模式用来对GUI中的动作做侦听。Swing GUI的例子就表明了动作侦听是怎样实现观察者模式的。
下面是一个猎头的典型例子。这个图中有2个角色-猎头和求职者。求职者先在猎头处注册,当有新的工作机会时猎头就会通知求职者。
观察者模式用来对GUI中的动作做侦听。Swing GUI的例子就表明了动作侦听是怎样实现观察者模式的。
下面是一个猎头的典型例子。这个图中有2个角色-猎头和求职者。求职者先在猎头处注册,当有新的工作机会时猎头就会通知求职者。
示例代码如下:
#include <list>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Subject;
//Observer接口:抽象观察者
class Observer{
private:
string name;
public:
Observer(string n):name(n){
}
string getName(){
return name;
}
virtual void update(Subject* s)=0;
};
//Subject接口:抽象主题
class Observer;
class Subject{
protected:
//定义一个观察者集合用于存储所有观察者对象
list<Observer*> observerList;
public:
//注册方法,用于向观察者集合增加一个观察者
virtual void registerObserver(Observer* observer){
cout<<observer->getName()<<"向猎头注册"<<endl;
observerList.push_back(observer);
};
//注销方法,用于在观察者集合中删除一个观察者
virtual void removeObserver(Observer* observer){
list<Observer*>::iterator iter=find(observerList.begin(),observerList.end(),observer);
if(iter==observerList.end()){
cout<<observer->getName()<<"没有向猎头注册"<<endl;
}else{
cout<<(*iter)->getName()<<"取消向猎头注册"<<endl;
observerList.erase(iter);
}
return;
}
//声明抽象通知方法
virtual void notifyAllObserver()=0;
};
//Hunter类:具体主题
class Hunter : public Subject{
private:
list<string> jobs;
public:
Hunter(){}
//实现通知方法
void notifyAllObserver(){
list<Observer*>::iterator iter=observerList.begin();
//遍历观察者集合,调用每一个观察者的响应方法
while(iter!=observerList.end()){
(*iter)->update(this);
++iter;
}
}
//添加工作
void addJob(string jobName){
jobs.push_back(jobName);
//观察目标发生变化,通知所有观察者
notifyAllObserver();
}
//得到工作列表
void getJobs(){
list<string>::const_iterator iter=jobs.begin();
cout<<"可提供的工作最新列表:"<<endl;
while(iter!=jobs.end()){
cout<<*iter<<endl;
++iter;
}
}
};
//JobSeeker:具体观察者
class JobSeeker : public Observer{
public:
JobSeeker(string name):Observer(name){
}
void update(Subject *s){
cout<<getName()<<"得到猎头通知!"<<endl;
((Hunter*)s)->getJobs();//此处没有针对抽象编程
}
};
//客户端测试代码
int main(){
Hunter *hunter=new Hunter();
Observer *observer1,*observer2;
observer1=new JobSeeker("Mike");
observer2=new JobSeeker("Jeff");
hunter->registerObserver(observer1);
hunter->registerObserver(observer2);
hunter->addJob("Google Job");
hunter->addJob("Yahoo Job");
hunter->removeObserver(observer1);
return 0;
}
程序运行结果: