1、动机
在软件构建过程中,我们需要为某种对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好的抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系,从而实现软件体系的松耦合。
2、show me the code(注:伪码的形式)
假设我们现在有一个需求:把一个上G的资料分割成几个1百兆的文件,txtFilePath代表文件路径,txtFileNumber表示分割的个数
接下来我们想加一个进度条显示分割进展如何(假设ProgressBar是平台库支持的进度条显示类):
初学者可以使用上述方法解决问题,但是这种添加方式违背了依赖倒置原则,我们尝试加一个接口(观察者)用来看执行分割进度:
把实现细节放进DoProgress中:
上述我们就完成了对通知消息的解耦(IProgress接口就是观察者),看看我们这个条件的定义是“所有的观察者”,尝试添加多个观察者看看会怎么样:
添加多个观察者:
代码整理:
class MainForm
{
public:
string path;
int num;
public:
MainForm(){//给路径path和分割数量num赋值等一些初始化操作}
DoStep()
{
Notifier not; //定义一个订阅对象
FileSplitter splitter(path, num);
splitter.addOberver(¬); //添加观察者(订阅通知)
splitter.split();//文件分割
//...
splitter.deleteObserver(¬); //取消订阅
splitter.GetPrgress();
}
};
class FileSplitter
{
public:
string m_path;
int m_size;
vector<IOberver *> m_iOberver;//多个观察者
public:
FileSplitter(string path,int num):m_path(path),m_size(num){}
void split()
{
for(int i = 0; i < m_size; i++)
{
float value=(float)(i + 1) / (float)(m_size);
update(value); //将进度传过来更新
}
}
void addOberver(IOberver *ob)
{
m_iOberver->push_back(ob);
}
void deleteObserver(IOberver *ob)
{
m_iOberver->remove(ob);
}
void update(float value)
{
for(int num=0;num<m_iOberver.size();num++)
{
m_iOberver[num]->Update(value);//对每一个添加订阅的观察者的状态均更新
}
}
void GetPrgress()
{
for(auto ii: m_iOberver)
{
cout << ii->GetState() << ", ";
}
}
};
class IOberver
{
public:
virtual void Update(float value) = 0;
virtual void GetState() = 0;
virtual ~IOberver(){}
};
class Notifier: public IOberver
{
public:
ProgressBar *m_progressBar;
public:
void Update(float value)
{
m_progressBar->setValue(value);
}
void GetState()
{
m_progressBar->showBar();
}
};
3、模式定义:
4、观察者模式结构
5、要点总结
(1)使用面向对象的抽象,Observer使得我们可以独立的改变目标与观察者,从而是两者的依赖关系达到松耦合
(2)目标发送通知是,无需指定观察者,通知(可以携带通知信息作为参考)可以自动传播
(3)观察者自己决定是否需要订阅通知,目标对象对此一无所知
(4)Observer是基于事件的UI框架中非常常用的设计模式,也是MVC模式的一个重要组成部分