“设计模式”学习之八:备忘录、观察者与状态(行为型)

一、备忘录(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模式)。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值