// // // // // // // // //
///2013.2.15
// // // // // // // // //
如果要给设计模式按照难易程度评级别的话,
之前的设计模式大多是一星或一星半(当然,也有部分原因是我没有深入)。
但是今天要讲的这个设计模式,
难度应该是三星(Not samsung :-P)。
但与此相比,
其实用性却是五星级别的。
因为无所不在的MVC结构(Model/View/Control),
就是这个设计模式最好的实例。
当然,
因为本文的面向对象是初学者,
故笔者在这里不打算使用MVC作为讲解。
如果想要了解更多关于MVC,请戳维基百科。
除此之外,
这个设计模式还有一个别名——发布/订阅模式。
运气不错,
我之前学习的《C#3.0设计模式》一书中就是使用这个例子来进行模式拆分讲解的:
最简单的形容就是网页中的RSS订阅了,
不过话说在这个时代这种订阅模式已经不再风靡,
取而代之的是微博2.0时代。
众所周知,
微博中我们每个人都是博主的同时也是粉丝。
我们可以关注别人,
也可以被关注。
从粉丝角度来讲,
每一个粉丝都是一个观察者(Observer),
他们都会订阅(关注)你(的微博),
当他们关注你的时候,
你将他们放到一个粉丝列表之中——我们称之为Attach,
然后在这个粉丝列表中,
每一次你发一条新微博(更新一次状态),
在这个粉丝列表中的人都将接收到这条通知(显示在微博消息中),
这个过程我们称之为Notify,
然而当他发现你有时候会刷屏的时候,
会很生气,
于是决定将你取消关注——我们称之为Detach。
于是他也将从你的粉丝列表中消失,
这样当你再次发送新微博的时候,
他将不再接收。
【核心】订阅者订阅发布者的发布(请连续念三遍,是不是会不经意间说出布丁布丁?;-P)。
UML图:
不得不讲解一下这张UML图:
首先是请大家注意Observer左边的那个小星星,
代表Subject类中包含Observer集合(数个对象)。
Subject的三个接口及其对应关系如下:
Attach——新增观察者到Observer集合中。
Detach——将已存在观察者移除。
Notify——利用Observer对象的Update来通知本Subject已经发生变化。
ConcreteSubject类中的Set/GetState用于设置/获取此Subject的状态。
除此之外,
为什么会有两个ConcreteObserver(A/B)呢?
因为这是为了表明此模式适用于Subject具有多个观察者类别的情形。
比如说你的粉丝A与粉丝B不可能是同一个账号吧?
这样每个ConcreteObserver都会拥有自己的Update。
示例代码:
【大致思路】
FansA与FansB都关注Jay,当Jay发送状态时,他们的微博界面都会显示更新。
Subject.h
#ifndef _SUBJECT_H_ #define _SUBJECT_H_ #include<list> #include<string> class Observer; class Subject { public: Subject(); ~Subject(){} void Attach(Observer* obj); void Detach(Observer* obj); void Notify(); virtual std::string getState() = 0; virtual void setState(const std::string& s) = 0; private: std::list<Observer*>* fansList; }; class ConcreteSubject:public Subject { public: ConcreteSubject(); ~ConcreteSubject(){} virtual std::string getState(); virtual void setState(const std::string& s); private: std::string state; }; #endif
Subject.cpp
#include"Subject.h" #include"Observer.h" #include<string> #include<list> using namespace std; Subject::Subject() { fansList = new list<Observer*>; } void Subject::Attach(Observer* obj) { //Add to list. fansList->push_back(obj); } void Subject::Detach(Observer* obj) { //Remove from list. if(obj != nullptr) fansList->remove(obj); } void Subject::Notify() { list<Observer*>::iterator iter; //Notify everyone on the list. iter = fansList->begin(); for(;iter != fansList->end();iter++) { (*iter)->Update(this); } } ConcreteSubject::ConcreteSubject() { state = ""; } string ConcreteSubject::getState() { return state; } void ConcreteSubject::setState(const string& s) { state = s; }
Observer.h
#ifndef _OBSERVER_H_ #define _OBSERVER_H_ #include<string> #include"Subject.h" class Observer { public: Observer(){} ~Observer(){} virtual void Update(Subject* s); protected: std::string state; Subject* subject; }; class FansA: public Observer { public: FansA(Subject* s); ~FansA(); virtual void Update(Subject* s); }; class FansB: public Observer { public: FansB(Subject* s); ~FansB(); virtual void Update(Subject* s); }; #endif
Observer.cpp
#include"Observer.h" #include<iostream> using namespace std; void Observer::Update(Subject* s) { cout<<"Got a new Message:"<<endl; } FansA::FansA(Subject* s) { s->Attach(this); subject = s; } FansA::~FansA() { subject->Detach(this); if(subject != nullptr) delete subject; } void FansA::Update(Subject* s) { cout<<"FansA "; Observer::Update(s); cout<<s->getState()<<endl; cout<<""<<endl; } FansB::FansB(Subject* s) { s->Attach(this); subject = s; } FansB::~FansB() { subject->Detach(this); if(subject != nullptr) delete subject; } void FansB::Update(Subject* s) { cout<<"FansB "; Observer::Update(s); cout<<s->getState()<<endl; cout<<""<<endl; }
main.cpp
#include"Observer.h" int main() { Subject* Jay = new ConcreteSubject(); Observer* fansA = new FansA(Jay); Observer* fansB = new FansB(Jay); Jay->setState("Happy new year,everyone,I'm Super Jay"); Jay->Notify(); return 0; }
输出结果:
注意事项:
代码使用list来作为存储容器,
同样也可以使用其他类型的集合来装载。
需要注意的是,
在Subject的构造函数中一定要先对这个list进行初始化才能使用。
还有在Observer的析构函数中,
可以看到要先进行detech然后再删除,
这样就避免了内存分配问题。