观察者模式,对象行为型模式,又称为依赖模式,发布订阅模式。当一个对象状态发生改变时,通过通知的形式,让依赖于它的多个对象自动得进行更新。例如JAVA中的Listener ,C#中的Event。
在合理的依赖关系中,下层结构不应该依赖于上层结构。但是在某些情况中,需要从底层的方法将上层的对象进行更新。一般的解决方式是将上层的对象以参数的形式不断的传递到底层,而观察者模式中,在底层方法里发出通知到接口中,而上层对象通过对接口的实现,将内容更新至其属性里。
在案例中,有一个复制大文件时更新进度条进度的 需求。
class FileCopy{
string srcpath;
string dispath;
ProgressBar * progressBar;
public:
FileCopy(string s,string d,ProgressBar & p):filepath(s),dispath(d),progressBar(p){}
void copy(){
//1 创建目标文件,读取源文件
disfile =createfile(dispath)
srcfile =readfile(srcpath)
....
double totalsize = srcfile.totalsize();
double sum = 0;
while(content=srcfile.readline()){
//按行写入文件
disfile.write(content);
//写文件后更新进度条
sum +=content.size;
progressBar->setvalue(sum/totalsize);
}
}
}
//------------界面---------------
class MainForm {
ProgressBar bar;
public:
void button_Click(){
//.....
FileCopy fc(srcfile,disfile,&bar);
fc.copy();
}
}
上述的代码设计中,虽然实现了功能,但弊端是显而易见的。
1.下层的FileCopy 依赖于上层 mainform的对象,FileCopy类无法复用于控制台,网页等其他项目工程
2.当需求变为“不用进度条显示进度,改用Label直接以百分比展示进度”,那么自上而下的要把对象类型进行改造,并且progressBar有setvalue方法,而其他控件可能没有。改造起来复杂度高。
3.这个方法当前只能支持一个进度条,如果想要支持多个不同的控件,那就只能要扩展参数,可维护性会变差。
在如下代码中,展示了以观察者模式的模式设计实现相同的需求。摆脱了底层依赖于高层,达到稳定与变化的分离,可以将类和接口原封不动地应用与其他项目。提高的了代码的复用性,易维护性,易扩展性。 以通知地形式,开放通知接口,让应用程序去实现该接口达到更新进度条的目的。
//通知接口
class INotice{
virtual void notify(double)=0;
}
class FileCopy{
string srcpath;
string dispath;
vector<INotice*> notices; //通知对象集合
public:
FileCopy(string s,string d):filepath(s),dispath(d){}
void add(INotice * in){notices.push_back(in);}
void copy(){
//1 创建目标文件,读取源文件
disfile =createfile(dispath)
srcfile =readfile(srcpath)
....
double totalsize = srcfile.totalsize();
double sum = 0;
while(content=srcfile.readline()){
//按行写入文件
disfile.write(content);
//写文件后更新进度条
sum +=content.size;
//给所有依赖发出通知
copynotify(sum/totalsize);
}
}
protected:
void copynotify(double value){
for(notice :notices){
notice->notify(value);
}
}
}
//------------界面,实现INotice接口方法---------------
class MainForm:public INotice{
ProgressBar bar;
public:
void button_Click(){
//.....
ChildForm *cform = new ChildForm ();
FileCopy fc(srcfile,disfile);
fc.add(this);
fc.add(cform );
fc.copy();
}
virtual void notify(double value){
bar.setvalue(value);
}
}
class ChildForm:public INotice{
Label label;
virtual void notify(double value){
bar.settitle(value);
}
}