设计模式之实现---观察者模式
/**********************************************************
CPaper.h
**********************************************************/
#pragma once
#include <iostream>
#include <string>
using namespace std;
class CPaper{
public:
string context;
public:
CPaper(){}
CPaper(const string& _cont){
context = _cont;
}
CPaper(const CPaper& cInst){
context = cInst.context;
}
CPaper& operator=(const CPaper& rhs){
context = rhs.context;
return *this;
}
void viewPaper(){
cout<<context<<endl;
}
bool isEmpty(){
return context == "";
}
};
/***************************************************
Observer.h
***************************************************/
/*
实现报社与报纸订阅者之间的: 主题与观察者模式
这里只实现,一个主题有多个观察者,而一个观察者只有一个主题的情况。
如果一个观察者有多个主题,情况也类似。
by 何戬, hejian@cad.zju.edu.cn
*/
#pragma once
#include "CPaper.h"
#include <iostream>
#include <string>
using namespace std;
class PaperProvider;
/////////////////////////
//观察者的基类,针对接口编程
class CObserver
{
public:
virtual void Update(const CPaper& _inPaper) = 0;
};
///////////////////////
//再提供专门为报纸订阅者写的基类
//之所以之CObserver继承,我是为是避免使用多重继承
//因为在C++中实现多重继承的代码是比较大的,按照书里的做法,这个类也是一个接口。
class CSubscriber : public CObserver
{
public:
CPaper m_paper;
string m_SubscriberName;
//假设只能订阅一个主题
PaperProvider* m_paperProvider;
public:
virtual void display() = 0;
virtual void addProvider(PaperProvider *paper);
virtual void removeProvider();
string& getSubScriberName(){
return m_SubscriberName;
}
CSubscriber(PaperProvider *paper, string name) : m_paperProvider(paper), m_SubscriberName(name){}
};
/////////////////////////////////
//实观察者类(concrete observer)
class SimpleEditionSubscriber : public CSubscriber
{
public:
//这个就是针对接口编程的好处
void Update(const CPaper& _inPaper){
//假设这个简易版本的报名,只显示第一个字符
m_paper.context = _inPaper.context[0];
//这步是为了简单才直接调用了,其实应该是订阅者自己检查是否有新的报纸,自己才调用display的
display();
}
void display(){
cout<<"I'm a simple edition subscriber!"<<endl;
m_paper.viewPaper();
}
public:
SimpleEditionSubscriber(PaperProvider *paper, string name);
};
/////////////////////////////////
//实观察者类(concrete observer)
class FullEditionSubscriber : public CSubscriber
{
public:
void Update(const CPaper& _inPaper){
m_paper = _inPaper;
//这步是为了简单才直接调用了,其实应该是订阅者自己检查是否有新的报纸,自己才调用display的
display();
}
void display(){
cout<<"I'm "<<m_SubscriberName<<" : a Full edition subscriber!"<<endl;
cout<<">>>>>>Begin read"<<endl;
m_paper.viewPaper();
cout<<">>>>>>End read"<<endl;
}
public:
FullEditionSubscriber(PaperProvider *paper, string name);
};
/********************************************************
Subjects.h
********************************************************/
#pragma once
#include <iostream>
#include <list>
#include "CPaper.h"
#include "Observer.h"
using namespace std;
/*
只实现了单个报刊提供商,
如果要实现多个报刊提供商,那我们只需要再做一个纯虚基类,这个基类继承CSubject的三个主题接口,
其它所有的实主题类都派生自这个纯虚基类
*/
///////////////////////////////////////////////////////
//这是一个主题类的基类接口
//实现三个最基本的主题类操作
//如果有多个主题,那么必须都从这个类继承接口。
class CSubject
{
public:
//注册一个新的观察者
virtual void registerObject(CSubscriber* newSubscriber) = 0;
//删除一个已有的观察者
virtual void removeObject(CSubscriber* delSubscriber) = 0;
//通知一个已有的观察者
virtual void notifyObject() = 0;
};
//////////////////////////////////////////////////////
//定义一个新的实观察者
class PaperProvider : public CSubject
{
private:
list<CSubscriber*> m_Subscriber;
//假设只能做一份报纸
CPaper m_paper;
public:
void registerObject(CSubscriber* newSubscriber){
if(!newSubscriber)
return;
m_Subscriber.push_back(newSubscriber);
}
void removeObject(CSubscriber* delSubscriber){
if(!delSubscriber)
return;
//通过name来删除观察者,其实应该通过id来删除,
//为了简单起见所以我用了name,而且name不会重复
string name = delSubscriber->getSubScriberName();
list<CSubscriber*>::iterator ibeg;
list<CSubscriber*>::iterator iend = m_Subscriber.end();
for(ibeg = m_Subscriber.begin(); ibeg != iend; ++ibeg)
{
CSubscriber* cpos = *ibeg;
if( name == cpos->getSubScriberName()){
//delete cpos;
//cpos = NULL;
ibeg = m_Subscriber.erase(ibeg);
return;
}
}
}
void notifyObject(){
if( m_paper.isEmpty() )
return;
//通知每一个objserver
list<CSubscriber*>::iterator ibeg;
list<CSubscriber*>::iterator iend = m_Subscriber.end();
for(ibeg = m_Subscriber.begin(); ibeg != iend; ++ibeg)
{
CSubscriber* cpos = *ibeg;
cpos->Update(m_paper);
}
}
void PrintPaper(const string& str){
//新报纸出印刷完毕
m_paper = CPaper(str);
//发送给每个观察者(订阅者)
notifyObject();
}
public:
//这里和书上不一样, Java自己提供了垃圾回收机制,但是C++中我们必须手动来清理这些对象
~PaperProvider(){
/* 想了一下,还是留给用户去删除吧。因为也许用户还定了其它报纸呢?
list<CSubscriber*>::iterator ibeg;
list<CSubscriber*>::iterator iend = m_Subscriber.end();
for(ibeg = m_Subscriber.begin(); ibeg != iend; ++ibeg)
{
CSubscriber* cpos = *ibeg;
delete cpos;
cpos = NULL;
}
*/
m_Subscriber.empty();
}
};
/***************************************************************
Observer.cpp
***************************************************************/
/*
没办法,这个模式要相互包含头文件,所以只能使用前置定义了
by 何戬, hejian@cad.zju.edu.cn
*/
#include <iostream>
using namespace std;
#include "Subjects.h"
#include "Observer.h"
///////////////////////////////////////////////////////////////////////////////
/////////////////////////CSubscriber Implementation////////////////////////////
//订阅新主题
void CSubscriber::addProvider(PaperProvider *paper)
{
//先把退订原先的主题
removeProvider();
m_paperProvider = paper;
if(m_paperProvider){
m_paperProvider->registerObject(this);
}
}
//退订主题
void CSubscriber::removeProvider()
{
if( !m_paperProvider )
return;
m_paperProvider->removeObject(this);
}
///////////////////////////////////////////////////////////////////////////////
/////////////////////////FullEditionSubscriber Implementation////////////////////////////
SimpleEditionSubscriber::SimpleEditionSubscriber(PaperProvider *paper, string name):
CSubscriber(paper, name)
{
if(!m_paperProvider)
return;
m_paperProvider->registerObject(this);
}
///////////////////////////////////////////////////////////////////////////////
/////////////////////////SimpleEditionSubscriber Implementation////////////////////////////
FullEditionSubscriber::FullEditionSubscriber(PaperProvider *paper, string name):
CSubscriber(paper, name)
{
if(!m_paperProvider)
return;
m_paperProvider->registerObject(this);
}
/***************************************************************
testObserver.cpp
***************************************************************/
/*
设计模式: 观察者模式
by 何戬 hejian@cad.zju.edu.cn
2009. 6. 1
*/
#include <iostream>
#include "Subjects.h"
#include "Observer.h"
using namespace std;
int main()
{
//
cout<<"::::::::::::The First Day:::::::::::::::"<<endl;
//生成一个主题(报刊提供商)
PaperProvider* paperPvd = new PaperProvider();
//生成一个全副面订阅者来订阅这个主题
FullEditionSubscriber *fullpaperSbc = new FullEditionSubscriber(paperPvd, "hejian");
//主题出版一份报纸
paperPvd->PrintPaper("hello the first free paper");
//
cout<<"::::::::::::The Second Day:::::::::::::::"<<endl;
//再多一个订阅者
FullEditionSubscriber *fullpaperSbc2 = new FullEditionSubscriber(paperPvd, "rabbit");
//主题出版一份报纸
paperPvd->PrintPaper("hello the first free paper: we have two full edtion subsribers");
//
cout<<"::::::::::::The Third Day:::::::::::::::"<<endl;
//多一个简化版订阅者,free的哈哈,因为只能看一份报纸的每一个字母。
SimpleEditionSubscriber* sp1 = new SimpleEditionSubscriber(paperPvd, "simple pig");
//主题出版一份报纸
paperPvd->PrintPaper("a simple pig come into!");
//
cout<<"::::::::::::The fourth Day:::::::::::::::"<<endl;
fullpaperSbc2->removeProvider();
//主题出版一份报纸
paperPvd->PrintPaper("a full edition subscriber is removed!");
delete sp1;
delete fullpaperSbc2;
delete fullpaperSbc;
delete paperPvd;
}