一、备忘录(Memento,别称“快照Snapshot”或“令牌Token”)
1、引言
该模式使用备忘录对象存储另外一个对象内部状态的快照(即某时刻历史状态),以备今后恢复、支持Undo机制等。例如,编辑Word文档的时候,会相应生成许多隐藏文件,这些其实就是这个文档不同时期的的快照,也就是Memento,只是序列化在了硬盘上保存。参考:http://www.cnblogs.com/Jax/archive/2007/04/10/707887.html
2、思路
《设计模式》给出了如下典型类图。其中,原发器Originator创建备忘录对象Memento,并经由负责人CareTaker将某些时刻的状态State保存至Memento,以备恢复。
备忘录对象Memento提供了两类接口:对Originator提供宽接口;对CareTaker及其它对象提供窄接口——只能传递对象,无法查看其内部成员如state。在C++中,可将Memento成员设为private,并将Originator声明为友元类 friend class Originator。
CareTaker只能有一个,但是Originator可以有多个,从而实现一个本子上记多条信息。
3、引申
上述典型模式对应于http://www.iteye.com/topic/503059中的宽接口或者白箱;而以下类图对应于窄接口或者黑箱实现。两者区别在于,由于负责人对象拿到的仅是抽象类IMemento的对象,因此可以确保其无法读出备忘录内部的状态。
另外,自述历史模式作为备忘录模式的一个变种,其特殊实现形式简单易懂,它可能是备忘录模式最为流行的实现形式。 它的思路是,将Memento类作为Originator的内部类,去除CareTaker的参与,由Originator自行负责存储状态至Memento。
4、应用提示
(1)实际应用中,可以引入vector等数据结构存储多个状态至Memento。
(2)Memento模式优点在于,把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。缺点包括,若需存储的内部状态很多,耗费内存。
(3)备忘录模式常常与命令Command模式和迭代Iterator模式一同使用。
二、观察者(Observer,别称“依赖Dependents”或“发布-订阅Publish-Subscribe”)
1、引言
通常该模式处理对象间存在一对多依赖关系的情况。比如,一个数据类对象有多种表现形式。
2、思路
如下图所示,目标Subject利用Attach() / Detach() 向List中添加观察者Observer,并通过Notify() 向各个观察者广播信息。然后,各具体观察者响应Update(),从具体目标ConcreteSubject中获取数据信息GetState()。
3、典型代码
// "Observer.h":头文件//
#ifndef _OBSERVER_H_
#define _OBSERVER_H_
#include <list>
#include <iostream>
#include <conio.h>
#include <string>
using namespace std;
typedef string State;
class Observer;
class Subject
{
private:
list <Observer *> *listObserver;
public:
Subject()
{
listObserver = new list<Observer*>;//在模板的使用之前一定要new,创建
}
virtual ~Subject(){}
virtual void Notify(); //通知 观察者
virtual void Attach(Observer* obv);//注册 观察者
virtual void Detach(Observer* obv);//注销 观察者
virtual State GetState()=0;
virtual void SetState(const State& st)=0;
};
class ConcreteSubject : public Subject
{
private:
State _stateSubject;
public:
ConcreteSubject()
{
_stateSubject = "";
}
~ConcreteSubject(){}
State GetState()
{
return _stateSubject;
}
void SetState(const State& st)
{
this->_stateSubject = st;
}
};
class Observer
{
public:
Observer(){}
virtual ~Observer(){}
virtual void Update(Subject *sub)=0;
protected:
State _stateObserver;
};
class ConcreteObserverA : public Observer
{
public:
ConcreteObserverA(Subject *sub)
{
this->_subject = sub;
_subject->Attach(this);
}
~ConcreteObserverA()
{
_subject->Detach(this);
if(_subject != NULL)
{
delete _subject;
}
}
void Update(Subject *sub)
{
_stateObserver = _subject->GetState();
cout<<"ConcreteObserverA :"<<_stateObserver<<endl;
}
public:
Subject* _subject;//传入Subject作为参数,这样可以让一个View属于多个的Subject。
};
class ConcreteObserverB : public Observer
{
public:
ConcreteObserverB(Subject *sub)
{
this->_subject = sub;
_subject->Attach(this);
}
~ConcreteObserverB()
{
_subject->Detach(this);
if(_subject != NULL)
{
delete _subject;
}
}
void Update(Subject *sub)
{
_stateObserver = _subject->GetState();
cout<<"ConcreteObserverB :"<<_stateObserver<<endl;
}
private:
Subject* _subject;
};
#endif //~_MEDIATOR_CHATROOM_H_
// Observer.cpp : 定义控制台应用程序的入口点。
// 两个观察者Observer _vs_ 一个目标物Subject
#include "stdafx.h"
#include "Observer.h"
void Subject::Notify()
{
list<Observer*>::iterator it;
it = listObserver->begin();
for (;it != listObserver->end();it++)
{
(*it)->Update(this);//广播的方式,通知各个观察者
}
}
void Subject::Attach(Observer* obv)
{
listObserver->push_back(obv);
}
void Subject::Detach(Observer* obv)
{
if (listObserver != NULL)
listObserver->remove(obv);
}
int _tmain(int argc, _TCHAR* argv[])
{
ConcreteSubject* sub = new ConcreteSubject();
ConcreteObserverA* objA = new ConcreteObserverA(sub);
ConcreteObserverB* objB = new ConcreteObserverB(sub);
sub->SetState("old");//目标物 设定当前的状态
sub->Notify();
objA->_subject->SetState("new");//由观察者ObserverA 接收状态的改变
sub->Notify();
_getch();/*等待按键继续*/
return 0;
}
运行情况截图如下:
4、应用提示
(1)该模式中,当目标Subject向各个观察者广播信息时,由观察者自己决定是处理还是忽略该信息。
(2)上述实例针对常见的一个目标多个观察者的情况,当然可以存在一个观察者依赖于多个目标的情况,此时需要扩展Update()以使观察者知道是那个目标送来的通知。当目标很多而观察者较少时,可以用一个关联查找机制(例如hash表)来维护目标到观察者之间的映射关系,时间换空间,以降低目标中存储观察者所带来的负担。
(3)到底由谁调用Notify()触发更新有两种情况:一是,目标对象在改变状态后自动调用;二是,由客户负责。上述实例采取第二种方式。
(4)可用Mediator封装复杂的更新语义,充当目标与观察者之间的中介。此时,该Mediator对象可用Singleton模式以确保其唯一和可全局访问性。
三、状态(State,别称“状态对象Objects for States”)
1、引言
当某个对象可能有多种不同的状态,并且当状态改变时,可能引起其响应行为的变化。这时可以考虑用状态模式,来替代switch/case多分支选择语句。尤其当状态的切换比较混乱和频繁时,该模式尤其管用,能够将状态切换逻辑与响应行为实现分离,方便扩展新状态。
2、思路
如下图所示,环境Context维护一个当前状态变量_state,状态切换只需要将_state赋予ConcreteStateA或B的实例对象即可。行为Handle()分别交由各个具体状态对象做出响应。
另外,具体状态类会引用Context对象,以便能够实现状态转换。
3、典型代码
参考:http://wenku.baidu.com/view/06b35522af45b307e87197db.html,定义一个俱乐部,它的顾客有三个状态:一般访客,普通会员,贵宾会员。每个顾客都有一个帐户,帐户上存放顾客的存款,根据存款的不同切换会员的级别,当顾客提请俱乐部的服务时,俱乐部将根据顾客的当前级别提供与其级别对应的服务内容。当然,顾客可以存入或消费,并及时修改他们的帐户。
// "State.h":头文件//
#ifndef _STATE_H_
#define _STATE_H_
#include <list>
#include <iostream>
#include <conio.h>
#include <string>
using namespace std;
typedef double Amount;//金额
class ClientState;//State抽象类;
//Context:俱乐部
class ClubContext
{
public:
ClientState *_currentState;//当前状态
string _strClientName;//账户名称
public:
ClubContext(string strClientName);
ClubContext(string strClientName,ClientState *clientState);
virtual ~ClubContext(){}
void Deposit(Amount amount);
void Cost(Amount amount);
void ClientService();
};
//State抽象类:顾客
class ClientState
{
public:
ClientState(){}
virtual ~ClientState(){}
virtual void Deposit(Amount amount)=0; //存钱
virtual void Cost(Amount amount)=0; //取钱
virtual void StateCheck()=0; //确认更新等级状态
virtual void ClientService()=0; //服务
enum nClientGrade {VISTOR = 0, MEMBER = 100, VIP = 1000};//各级别门槛
protected:
Amount _balance;//余额
Amount _discount;//折扣
// Amount _lowerLimit;//当前账户状态 金额下限
// Amount _upperLimit;//当前账户状态 金额上限
ClubContext* _clubContext;
};
//游客级别 具体顾客状态
class VistorClientState : public ClientState
{
public:
VistorClientState(Amount balance, ClubContext* clubContext)
{
_clubContext = clubContext;
_balance = balance;
_discount = 9;//折扣
}
~VistorClientState(){}
void Deposit(Amount amount);
void Cost(Amount amount);
void StateCheck();
void ClientService();
};
//普通会员级别 具体顾客状态
class MemberClientState : public ClientState
{
public:
MemberClientState(Amount balance, ClubContext* clubContext)
{
_clubContext = clubContext;
_balance = balance;
_discount = 8;//折扣
}
~MemberClientState(){}
void Deposit(Amount amount);
void Cost(Amount amount);
void StateCheck();
void ClientService();
};
//VIP会员级别 具体顾客状态
class VIPClientState : public ClientState
{
public:
VIPClientState(Amount balance, ClubContext* clubContext)
{
_clubContext = clubContext;
_balance = balance;
_discount = 6;//折扣
}
~VIPClientState(){}
void Deposit(Amount amount);
void Cost(Amount amount);
void StateCheck();
void ClientService();
};
#endif //~_STATE_H_
// State.cpp : 定义控制台应用程序的入口点。
// 三个具体等级状态
#include "stdafx.h"
#include "State.h"
ClubContext//
ClubContext::ClubContext(string strClientName)
{
this->_currentState = new VistorClientState(0.0, this);//初始创建 游客状态等级的客户
this->_strClientName = strClientName;
}
ClubContext::ClubContext(string strClientName,ClientState *clientState)
{
this->_currentState = clientState;
this->_strClientName = strClientName;
}
void ClubContext::Deposit(Amount amount)
{
_currentState->Deposit(amount);
}
void ClubContext::Cost(Amount amount)
{
_currentState->Cost(amount);
}
void ClubContext::ClientService()
{
_currentState->ClientService();
}
VistorClientState//
void VistorClientState::Deposit(Amount amount)
{
_balance += amount;
cout<<"您存入"<<amount<<"元;"<<"当前账户余额为"<<_balance<<"元"<<endl;
StateCheck();
}
void VistorClientState::Cost(Amount amount)
{
_balance -= amount;
cout<<"您取出"<<amount<<"元;"<<"当前账户余额为"<<_balance<<"元"<<endl;
StateCheck();
}
void VistorClientState::StateCheck()
{
/* if (balance > VISTOR && balance < MEMBER)
{
_clubContext = new
}
*/ if (_balance > MEMBER && _balance < VIP)
{
_clubContext->_currentState = new MemberClientState(_balance, _clubContext);
cout<<"您的账户已由“游客”升级为“普通会员”。"<<endl;
}
else if (_balance > VIP)
{
_clubContext->_currentState = new VIPClientState(_balance, _clubContext);
cout<<"您的账户已由“游客”升级为“VIP会员”。"<<endl;
}
}
void VistorClientState::ClientService()
{
cout<<"您当前可享受"<<_discount<<"折优惠。"<<endl;
}
MemberClientState//
void MemberClientState::Deposit(Amount amount)
{
_balance += amount;
cout<<"您存入"<<amount<<"元;"<<"当前账户余额为"<<_balance<<"元"<<endl;
StateCheck();
}
void MemberClientState::Cost(Amount amount)
{
_balance -= amount;
cout<<"您取出"<<amount<<"元;"<<"当前账户余额为"<<_balance<<"元"<<endl;
StateCheck();
}
void MemberClientState::StateCheck()
{
if (_balance < MEMBER)
{
_clubContext->_currentState = new VistorClientState(_balance, _clubContext);
cout<<"您的账户已由“普通会员”降级为“游客”。"<<endl;
}
else if (_balance > VIP)
{
_clubContext->_currentState = new VIPClientState(_balance, _clubContext);
cout<<"您的账户已由“普通会员”升级为“VIP会员”。"<<endl;
}
}
void MemberClientState::ClientService()
{
cout<<"您当前可享受"<<_discount<<"折优惠。"<<endl;
}
VIPClientState//
void VIPClientState::Deposit(Amount amount)
{
_balance += amount;
cout<<"您存入"<<amount<<"元;"<<"当前账户余额为"<<_balance<<"元"<<endl;
StateCheck();
}
void VIPClientState::Cost(Amount amount)
{
_balance -= amount;
cout<<"您取出"<<amount<<"元;"<<"当前账户余额为"<<_balance<<"元"<<endl;
StateCheck();
}
void VIPClientState::StateCheck()
{
/* if (balance > VISTOR && balance < MEMBER)
{
_clubContext = new
}
*/ if (_balance > MEMBER && _balance < VIP)
{
_clubContext->_currentState = new MemberClientState(_balance, _clubContext);
cout<<"您的账户已由“VIP会员”降级为“普通会员”。"<<endl;
}
else if (_balance < MEMBER)
{
_clubContext->_currentState = new VistorClientState(_balance, _clubContext);
cout<<"您的账户已由“VIP会员”降级为“游客”。"<<endl;
}
}
void VIPClientState::ClientService()
{
cout<<"您当前可享受"<<_discount<<"折优惠。"<<endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
ClubContext* club = new ClubContext("刘斌");//俱乐部
club->Deposit(100);
club->Deposit(300);
club->Deposit(1000);
club->ClientService();
club->Cost(100);
club->Cost(1000);
club->Cost(300);
_getch();/*等待按键继续*/
return 0;
}
运行情况截图如下:
4、应用提示
(1)该State模式中,状态的转换准则有两种定义方法:一是,若准则固定,可在环境Context中完全实现;二是,若追求灵活性,可让State子类指定它们的后继状态及切换时机。上节实例中,采用第二种方式在StateCheck()中实现。
(2)对于何时创建和销毁State对象的问题,有两种处理方式:一是,仅当需要时创建并随后销毁,适用于状态切换不频繁;二是,提前创建并始终不销毁。上节实例采用二。
(3)状态对象通常是Singleton,并且可被共享(Flyweight模式)。