设计模式之实现---观察者模式

/**********************************************************

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;
}

posted on 2012-11-06 10:14  bitbit  阅读(397)  评论(0编辑  收藏  举报