观察者模式在有些地方也叫做事件模式 ,
动机:
在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”-----一个对象(目标对象)的 状态发生改变,
所有依赖对象(观察者对象)都将得到通知,如果这样的依赖关系过于紧密将是软件不能很好的地狱变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系,从未实现软件体系结构的松耦合。
继续我们结构化的思维来写一段代码。
需求这样的:现在有一个文件夹下面有多个文件,现在要进行下载。并且需要显示一个进度条在界面上面。
大多数人第一之间实现功能的方式就是这样的。
class mainForm :public Form {
ProgressBar *progressBar; // 进度条控件 UI 控件
void buttonClick(){
FileDownload *file = new FileDownload(Dir ,progressBar);
file->downloadFile();
}
};
class FileDownload{
String mDir;
ProgressBar *mprogressBar;
FileDownload(String Dir,ProgressBar *progressBar){
mDir =Dir;
mprogressBar= progressBar;
}
public:
void downloadFile(){
// 遍历并且下载文件
for(int i = 0 ;i <fileCount;i++){
//...
if(NULL!=mprogressBar){
mprogressBar.setValus(i+1);
}
}
}
};
我们来看下到底有什么问题呢!
还记得设计原则吗 依赖倒置原则 高层模块不能依赖于底层模块 二者应该依赖于抽象。抽象不依赖实现细节,
实现细节应该依赖抽象,来看下我们的代码哪里产生不良的依赖呢?
Filedownload 类中的 ProgressBar 就是不良的编译时依赖 ProgressBar 就是实现细节,实现细节往往是多变的
今天我们可能使用这个控件来实现的,一定能保证以后一定还是以同样的方式进行进度显示的吗?
(比如 紧紧是控制台程序 只能用“。”来表示进度呢)
如过我们单纯的去考录 抽象出Filedownload的父类 可能发现这个思路根本不对 , 要思考我们到底需要什么 ,最终找到
只是需要一个通知 其实可以用抽象来表达,而不是具体的控件来表达。下面使我们提炼出一个抽象接口的实现伪代码:
class Iprogress{
virtual void doProgress(float value) = 0; // 表达抽象的通知
virtual ~Iprogress();
};
class mainForm :public Form , public Iprogress{
ProgressBar *progressBar; // 进度条控件 UI 控件
void buttonClick(){
FileDownload *file = new FileDownload(Dir ,this);
file->downloadFile();
}
void doProgress(float value){
progressBar.setValue(value) ;
}
};
class FileDownload{
String mDir;
ProgressBar *mprogressBar;
Iprogress *miprogress;//抽象的通知
FileDownload(String Dir ,Iprogress *iprogress){
mDir =Dir;
miprogress =iprogress;
}
public:
void downloadFile(){
// 遍历并且下载文件
for(int i = 0 ;i <fileCount;i++){
//...
if(NULL!=miprogress){
miprogress.doProgress(i+1);
}
}
}
};
说明一下C++ 本身是存在多继承的,但是由于经验的总结,出现问题太过复杂,所以高级语言是C++ 最终实践过来的
C++ 一般不会使用多继承 ,多继承使用的时候都是继承多个几口(虚类) 比如java 废弃了多继承,但是保留使用多接口的方式。
现在 将一个紧耦合的代码变成一个松耦合的代码 ,可以看一下 Filedownload 类当中 已经不存在界面类的progressBar 这样的
紧耦的关系 。这样的文件分割出来后 。我可以独立编译 可以放在任何的程序里面可以是界面程序也可以是控制台程序
就是通过一个抽象接口Iprogress 的隔离 ,实现了松耦合关系。
仔细看看设计的思路其实我们在重构的过程当中就实现了观察者模式 ,只不过我们只有一个观察者而已就是mainForm,
假如我们现需求继续变更 在一个界面需要得到这个通知同时一个控制台也需要这个通知。然而我们只支持了一个。
那么我就让FileDownload类可以支持多个观察者 。集合上场:
class Iprogress{
virtual void doProgress(float value) = 0; // 表达抽象的通知
virtual ~Iprogress();
};
class mainForm :public Form , public Iprogress{
ProgressBar *progressBar; // 进度条控件 UI 控件
cotrol *cto = new cotrol();
void buttonClick(){
FileDownload *file = new FileDownload(Dir ,this);
file->downloadFile();
file->addIprogress(this);
file->addIprogress(cto);
}
void doProgress(float value){
progressBar.setValue(value) ;
}
};
class cotrol :public Iprogress{
void doProgress(float value){
cout<<"." ;
}
};
class FileDownload{
String mDir;
/**集合来装载*/
List<Iprogress *> miprogressList;//抽象的通知
FileDownload(String Dir ){
mDir =Dir;
}
public:
void addIprogress( Iprogress *miprogress){
miprogressList.add(miprogress);
}
void removeIprogress( Iprogress *miprogress){
miprogressList.remove(miprogress);
}
void downloadFile(){
// 遍历并且下载文件
for(int i = 0 ;i <fileCount;i++){
//...
if(NULL!=miprogress){
onProgress(i+1);
}
}
}
void onProgress(float value){
List<Iprogress *>::Iterator ito = miprogressList.begin();
while(ito!=miprogressList.end())
(*ito)->doProgress(value);
ito++;
}
};
这时才是我们真正的观察者模式
模式的gof中定义:
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(subject)的状态发生改变时,
所有的依赖它的对象都得到通知并自动更新。
总结:使用面向对象的抽象,OBserver 模式使得我们可以独立的改变目标与观察者,从而使二者之间的依赖关系达到松耦合。
目标发送通知 不需要指定具体是谁,只需要向抽象发送,通知(可以携带信息)会自动传播。
观察者自己决定是否去订阅通知。目标对象对此一无所知。