本周学习了几种设计模式,包括Template, Strategy, Observer, Decorator和Bridge,觉得都有用。本文重点回顾一下Observer设计模式。
Observer设计模式主要应用在当目标对象的状态发生改变的时候,所有的依赖对象(也就是观察者对象)都将得到通知并自动更新。说到"通知",有嵌入式背景的同学可能马上就会想到用polling和interrupt这两种方法,但我们这里说的设计模式是在应用层,跟底层的OS和hardware无关,其实完全是两个不同领域的东西。
这里要注意的是,目标对象发送通知时,无需指定观察者,通知会自动传播给已经订阅通知的观察者。目标对象并不知道谁是观察者。我们想象这样一个例子,假设有几百万个客户都订阅了一个微信公众号,这个公众号每天都发送一些八卦新闻。客户可以自己选择订阅或退订,公众号的任务就是发送新闻而已,所有的客户在它看来都一样,没有区别。它一发送新闻,那些客户就收到新闻了。这个例子就是一个观察者模式的实际运用。
观察者模式的结构描述如下图:
这里Subject和Observer之间是松耦合。Subject的Notify()里面会调用一个loop,遍历所有的观察者并调用它们的update()函数。在Subject看来所有的观察者都是一样的,它们的update()没有区别。Observer通过订阅/退订,可以决定要不要收到通知。
Observer设计模式在基于时间的UI框架中用的很多。一个具体例子如下:
class IProgress{
public:
virtual void DoProgress(float value)=0;
virtual ~IProgress(){}
};
class FileSplitter
{
string m_filePath;
int m_fileNumber;
List<IProgress*> m_iprogressList; // 抽象通知机制,支持多个观察者
public:
FileSplitter(const string& filePath, int fileNumber) :
m_filePath(filePath),
m_fileNumber(fileNumber){
}
void split(){
//1.读取大文件
//2.分批次向小文件中写入
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);//发送通知
}
}
void addIProgress(IProgress* iprogress){
m_iprogressList.push_back(iprogress);
}
void removeIProgress(IProgress* iprogress){
m_iprogressList.remove(iprogress);
}
protected:
virtual void onProgress(float value){
List<IProgress*>::iterator itor=m_iprogressList.begin();
while (itor != m_iprogressList.end() )
(*itor)->DoProgress(value); //更新进度条
itor++;
}
}
};
class MainForm : public Form, public IProgress
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
ConsoleNotifier cn;
FileSplitter splitter(filePath, number);
splitter.addIProgress(this); //订阅通知
splitter.addIProgress(&cn); //订阅通知
splitter.split();
splitter.removeIProgress(this);
}
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
在这个例子中,FileSplitter是ConcreteSubject, MainForm和ConsoleNotifier都是客户(ConcreteObserver)。FileSplitter切割文件的时候,会调用onProgress()函数。onProgress()就相当于上面图里面的Notify(),里面会遍历m_iprogressList,这个就是客户列表。客户通过splitter.addIProgress()来订阅通知。
另外要注意的是,如果订阅的客户多了,Subject里面的Notify()就会比较慢,变成系统的瓶颈。看来这个Observer模式也是有局限性的。
下面是一个完整的observer模式的例子。代码参考自
https://blog.csdn.net/wuzhekai1985/article/details/6674984
#include <string>
#include <iostream>
using namespace std;
#include <list>
//pure virtual class, like JAVA's interface
class Observer
{
public:
Observer() {}
virtual ~Observer() {}
virtual void Update() {}
};
class Blog
{
public:
Blog() {}
virtual ~Blog() {}
void Attach(Observer *observer) { m_observers.push_back(observer); }
void Remove(Observer *observer) { m_observers.remove(observer); }
void Notify()
{
list<Observer*>::iterator iter = m_observers.begin();
for(; iter != m_observers.end(); iter++)
(*iter)->Update();
}
virtual void SetStatus(string s) { m_status = s; }
virtual string GetStatus() { return m_status; }
private:
list<Observer* > m_observers;
protected:
string m_status;
};
class BlogCSDN : public Blog
{
private:
string m_name;
public:
BlogCSDN(string name): m_name(name) {}
~BlogCSDN() {}
void SetStatus(string s) { m_status = "CSDN Notice : " + m_name + s; }
string GetStatus() { return m_status; }
};
class ObserverBlog : public Observer
{
private:
string m_name;
Blog *m_blog;
public:
ObserverBlog(string name,Blog *blog): m_name(name), m_blog(blog) {}
~ObserverBlog() {}
void Update()
{
string status = m_blog->GetStatus();
cout<<m_name<<"-------"<<status<<endl;
}
};
int main()
{
Blog *blog = new BlogCSDN("roufoo");
Observer *observer1 = new ObserverBlog("Tom", blog);
Observer *observer2 = new ObserverBlog("Jerry", blog);
blog->Attach(observer1);
blog->Attach(observer2);
blog->SetStatus("roufoo's new post published"");
blog->Notify();
delete blog;
delete observer1;
delete observer2;
return 0;
}