(Boolan) C++设计模式 第三周笔记(二)

状态模式
一、描述

概念:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

问题:
每个人、事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)。最简单的一个生活中的例子就是:
地铁入口处,如果你放入正确的地铁票,门就会打开让你通过。在出口处也是验票,如果正确你就可以 ok,否则就不让你通过(如果你动作野蛮,或许会有报警(Alarm),:)。

有限状态自动机(FSM)也是一个典型的状态不同,对输入有不同的响应(状态转移)。通常我们在实现这类系统会使用到很多的Switch/Case语句,Case某种状态,发生什么动作,Case 另外一种状态,则发生另外一种状态。但是这种实现方式至少有以下两个问题:
1)当状态数目不是很多的时候,Switch/Case 可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组的 Switch/Case 语句将是一件异常困难并且容易出错的事情。
2)状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。

模式选择

State 模式就是被用来解决上面列出的两个问题的,在 State 模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的 case 分支语句,并且这些分支依赖于对象的状态。State 模式将每一个分支都封装到独立的类中。

State 模式典型的结构图为:

备忘录模式
一、描述:

  没有人想犯错误,但是没有人能够不犯错误。犯了错误一般只能改过,却很难改正(恢复)。世界上没有后悔药,但是我们在进行软件系统的设计时候是要给用户后悔的权利(实际上可能也是用户要求的权利:),我们对一些关键性的操作肯定需要提供诸如撤销(Undo) 的操作。那这个后悔药就是 Memento 模式提供的。 

  Memento 模式的关键就是要在不破坏封装行的前提下,捕获并保存一个类的内部状态,这样就可以利用该保存的状态实施恢复操作。为了达到这个目标,可以在后面的实现中看到我们采取了一定语言支持的技术。

**Memento 模式的典型结构图为: **

二、实例

备忘录:Memento类
Memento.h

#ifndef __Memento__Memento__  
#define __Memento__Memento__  
  
#include <iostream>  
#include <string>   
using namespace std;  
  
class Memento  
{  
public:  
      
protected:  
      
private:  
    //这是最关键的地方,将 Originator 为 friend 类,可以访问内部信息,但是其他类不能访问  
    friend class Originator; typedef string State;  
    Memento();  
    Memento(const State& sdt); ~Memento();  
    void SetState(const State& sdt); State GetState();  
      
private:  
    State _sdt;  
};  

Memento.cpp

#include "Memento.h"  
typedef string State;  
Memento::Memento()  
{  
}  
Memento::Memento(const State& sdt)  
{  
    this->_sdt = sdt;  
}  
State Memento::GetState()  
{  
    return this->_sdt;  
}  
void Memento::SetState(const State& sdt)  
{  
    this->_sdt = sdt;  
}  

撤销返回操作:originator类
originator.h

#ifndef __Memento__Originator__  
#define __Memento__Originator__  
  
#include <iostream>  
#include <string>  
#include "Memento.h"  
using namespace std;  
class Originator  
{  
public:  
    typedef string State; Originator();  
    Originator(const State& sdt);  
    ~Originator();  
    Memento* CreateMemento();  
    void SetMemento(Memento* men);  
    void RestoreToMemento(Memento* mt);  
    State GetState();  
    void SetState(const State& sdt);  
    void PrintState();  
      
protected:  
private:  
    State _sdt;  
    Memento* _mt;  
};  

Originator.cpp


#include "Originator.h"  
typedef string State;  
  
Originator::Originator()  
{  
    this->_sdt = "";  
    this->_mt = 0;  
}  
Originator::Originator(const State& sdt)  
{  
    this->_sdt = sdt;  
    this->_mt = 0;  
}  
Originator::~Originator()  
{  
      
}  
  
Memento* Originator::CreateMemento()  
{  
    return new Memento(_sdt);  
}  
  
State Originator::GetState()  
{  
    return this->_sdt;  
}  
  
void Originator::SetState(const State& sdt)  
{  
    this->_sdt = sdt;  
}  
  
void Originator::PrintState()  
{  
    cout<<this->_sdt<<"....."<<endl;  
}  
  
void Originator::SetMemento(Memento* men)  
{  
      
}  
  
void Originator::RestoreToMemento(Memento* mt)  
{  
    this->_sdt = mt->GetState();  
}  

主程序:main
main.cpp

#include <iostream>  
#include "Memento.h"  
#include "Originator.h"  
using namespace std;  
  
int main(int argc, const char * argv[])  
{  
  
    // insert code here...  
    Originator *myOriginator = new Originator();  
    myOriginator->SetState("备忘状态old");//备忘前状态  
    myOriginator->PrintState();  
      
    Memento *myMemento = myOriginator->CreateMemento();//将状态备忘  
      
    myOriginator->SetState("修改状态new");//修改状态  
    myOriginator->PrintState();  
    myOriginator->RestoreToMemento(myMemento);//恢复修改前状态  
    myOriginator->PrintState();  
      
      
    std::cout << "Hello, World!\n";  
    return 0;  
}  

Memento 模式的关键就是 friend class Originator;我们可以看到,Memento 的接口都声明为 private,而将 Originator 声明为 Memento 的友元类。我们将 Originator 的状态保存在 Memento 类中,而将 Memento 接口 private 起来,也就达到了封装的功效。 在 Originator 类中我们提供了方法让用户后悔:RestoreToMemento(Memento* mt);我们可以 通过这个接口让用户后悔。在测试程序中,我们演示了这一点:Originator 的状态由 old 变为 new 最 后又回到了 old。

命令模式
一、描述:

  命令模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。应该是一个比较简单的模式了。

** Command 模式的典型结构图为: **

  Command 模式结构图中,将请求的接收者(处理者)放到 Command 的具体子类ConcreteCommand 中,当请求到来时(Invoker 发出 Invoke 消息激活 Command 对象),ConcreteCommand 将处理请求交给 Receiver 对象进行处理。 

二、实例:
描述:
今天讲命令模式,这个模式从名字上看就很简单,命令嘛,老大发命令,小兵执行就是了,确实是这个意思,但是更深化了,用模式来描述真是是世界的命令情况。正在看这本书的你,我猜测分为两类:已经工作的和没有工作的,先说没有工作的,那你为啥要看这本书,为了以后工作呗,只要你参见工作,你肯定会待在项目组,那今天我们就以项目组为例子来讲述命令模式。
我是我们部门的项目经理,就是一个项目的头,在中国做项目,项目经理就是什么都要懂,什么都要管,做好了项目经理能分到一杯羹,做不好都是你项目经理的责任,这个是绝对的,我带过太多的项目,行政命令一压下来,那就一条道,做完做好!我们虽然是一个集团公司,但是我们部门是独立核算的,就是说呀,我们部门不仅仅为我们集团服务,还可以为其他甲方服务,赚取更多的外快,所以俺们的工资才能是中上等。在 2007 年我带领了一个项目,比较小,但是钱可不少,是做什么的呢?为一家旅行社建立一套内部管理系统,管理他的客户、旅游资源、票务以及内部管理,整体上类似一个小型的 ERP 系统,门店比较多,员工也比较多,但是需求比较明确,因为他们之前有一套自己购买的内部管理系统,这次变动部分模块基本上是翻版,而且旅行社有自己的IT 部门,比较好相处,都是技术人员,没有交流鸿沟嘛。
这个项目的成员分工也是采用了常规的分工方式,分为需求组(Requirement Group,简称 RG)、美工组(Page Group,简称 PG)、代码组(我们内部还有一个比较优雅的名字:逻辑实现组,这里使用大家经常称呼的名称吧,英文缩写叫 Code Group,简称 CG),总共加上我这个项目经理正好十个人,刚开始的时候客户(也就是旅行社,甲方)还是很乐意和我们每个组探讨,比如和需求组讨论需求,和美工讨论页面,和代码组讨论实现,告诉他们修改这里,删除这里,增加这些等等,这是一种比较常见的甲乙方合作模式,甲方深入到乙方的项目开发中,我们把这个模式用类图表示一下:

但是问题来了,我们修改可以,但是每次都是叫一个组去,布置个任务,然后出计划,次次都这样,如果让你当甲方也就是客户,你烦不烦?而且这种方式很容易出错误呀,而且还真发生过,客户把美工叫过去了,要删除,可美工说需求是这么写的,然后客户又命令需求组过去,一次次的折腾,客户也烦躁了,于是直接抓住我这个项目经理说:
“我不管你们内部怎么安排,你就给我找个接头人,我告诉他怎么做,删除页面了,增加功能了,你们内部 怎么处理,我就告诉他我要干什么就成了...”我一听,好呀,这也正是我想要的,我项目组的兄弟们也已经受不了了,于是我改变了一下我的处理方式, 看看类图(类图稍微更改下名称):

注释:

main(),客户
Invoker,命令接收者,如项目经理
IGroup,执行者接口
CRequirementGroup,需要组
CPageGroup,美工组
CCodePage,代码组
ICommand,命令接口
CAddRequirementCommand,执行增加一项需求的命令(Execute函数,将调用CRequirementGroup的多个命令。来组合执行用户发出的命令。)
CDeletePageCommand,执行删除一个页面的命令

命令接收者:InvokerInvoker.h

#ifndef __Command__Invoker__  
#define __Command__Invoker__  
  
#include <iostream>  
#include "ICommand.h"  
  
class CInvoker  
{  
public:  
    CInvoker(void);  
    ~CInvoker(void);  
    void SetCommand(ICommand *pcommand);  
    void Action();  
private:  
    ICommand *m_pCommand;  
};  
  

Invoker.cpp

#include "Invoker.h"  
CInvoker::CInvoker(void)  
{  
}  
CInvoker::~CInvoker(void)  
{  
}  
void CInvoker::SetCommand( ICommand *pcommand )  
{  
    this->m_pCommand = pcommand;  
}  
void CInvoker::Action()  
{  
    this->m_pCommand->Execute();  
}  

命令接口:IComman类
ICommand.h


#ifndef Command_ICommand_h  
#define Command_ICommand_h  
#include "RequirementGroup.h"  
#include "PageGroup.h"  
#include "CodeGroup.h"  
  
class ICommand {     
public:  
      
    ICommand(void)  
    {  
        m_prg = new CRequirementGroup();  
        m_ppg = new CPageGroup();  
        m_pcg = new CCodeGroup();  
    }  
    virtual ~ICommand(void)  
    {  
        delete m_prg;  
        delete m_ppg;  
        delete m_pcg;  
    }  
    virtual void Execute() = 0;  
      
protected:  
      
    CRequirementGroup *m_prg;  
    CPageGroup *m_ppg;  
    CCodeGroup *m_pcg;  
      
};  
  

执行增加一项需求的命令:CAddRequirementCommand类
CAddRequirementCommand.h


#ifndef __Command__AddRequirementCommand__  
#define __Command__AddRequirementCommand__  
  
#include <iostream>  
#include "ICommand.h"  
class CAddRequirementCommand :public ICommand  
{  
public:  
    CAddRequirementCommand(void);  
    ~CAddRequirementCommand(void);  
    void Execute();  
};  
  

AddRequirementCommand.cpp

#include "AddRequirementCommand.h"  
  
CAddRequirementCommand::CAddRequirementCommand(void)  
{  
}  
CAddRequirementCommand::~CAddRequirementCommand(void)  
{  
}  
void CAddRequirementCommand::Execute()  
{  
    //执行增另一项需求的命令  
    this->ICommand::m_prg->Find();  
      
    //增加一份需求  
    this->ICommand::m_prg->Add();  
      
    //给出计划  
    this->ICommand::m_prg->Plan();  
}  

执行删除一个页面的命令 :CDeletePageCommand类
CDeletePageCommand.h


#ifndef __Command__DeletePageCommand__  
#define __Command__DeletePageCommand__  
  
#include <iostream>  
#include "ICommand.h"  
  
class CDeletePageCommand :  
public ICommand  
{  
public:  
    CDeletePageCommand(void);  
    ~CDeletePageCommand(void);  
    void Execute();  
};  
  

DeletePageCommand.cpp

**[cpp]** [view plain](http://blog.csdn.net/rexuefengye/article/details/13004205#) [copy](http://blog.csdn.net/rexuefengye/article/details/13004205#)

#include "DeletePageCommand.h"  
  
CDeletePageCommand::CDeletePageCommand(void)  
{  
}  
CDeletePageCommand::~CDeletePageCommand(void)  
{  
}  
void CDeletePageCommand::Execute()  
{  
    //执行增另一项需求的命令  
    this->ICommand::m_ppg->Find();  
      
    //增加一份需求  
    this->ICommand::m_ppg->Delete();  
      
    //给出计划  
    this->ICommand::m_ppg->Plan();  
}  

执行者接口:IGroup
IGroup.h

#ifndef Command_IGroup_h  
#define Command_IGroup_h  
class IGroup  
{  
public:  
    IGroup(void){}  
    virtual ~IGroup(void){}  
    virtual void Find() = 0;  
    virtual void Add() = 0;  
    virtual void Delete() = 0;  
    virtual void Change() = 0;  
    virtual void Plan() = 0;  
};  

需要组:CRequirementGroup类
CRequirementGroup.h

#ifndef __Command__RequirementGroup__  
#define __Command__RequirementGroup__  
  
#include <iostream>  
#include "IGroup.h"  
class CRequirementGroup:public IGroup  
{  
public:  
    CRequirementGroup(void);  
    ~CRequirementGroup(void);  
    void Find();  
    void Add();  
    void Delete();  
    void Change();  
    void Plan();  
};  
  
  

CRequirementGroup.cpp

#include "RequirementGroup.h"  
using std::cout;  
using std::endl;  
  
CRequirementGroup::CRequirementGroup(void)  
{  
}  
CRequirementGroup::~CRequirementGroup(void)  
{  
}  
void CRequirementGroup::Find()  
{  
    cout << "找到需求组..." << endl;  
}  
void CRequirementGroup::Add()  
{  
    cout << "客户要求增加一项需求..." << endl;  
}  
void CRequirementGroup::Delete()  
{  
    cout << "要求删除一项需求..." << endl;  
}  
void CRequirementGroup::Change()  
{  
    cout << "客户要求修改一项需求..." << endl;  
}  
void CRequirementGroup::Plan()  
{  
    cout << "客户要求需求变更计划..." << endl;  
}  

美工组:CPageGroup
CPageGroup.h


#ifndef __Command__CPageGroup__  
#define __Command__CPageGroup__  
  
#include <iostream>  
#include "IGroup.h"  
  
class CPageGroup :public IGroup  
{  
public:  
    CPageGroup(void);  
    ~CPageGroup(void);  
    void Find();  
    void Add();  
    void Delete();  
    void Change();  
    void Plan();  
};  
  

CPageGroup.cpp

#include "PageGroup.h"  
  
using std::cout;  
using std::endl;  
CPageGroup::CPageGroup(void)  
{  
}  
  
CPageGroup::~CPageGroup(void)  
{  
}  
  
void CPageGroup::Find()  
{  
    cout << "找到美工组..." << endl;  
}  
  
void CPageGroup::Add()  
{  
    cout << "客户要求增加一个页面..." << endl;  
}  
  
void CPageGroup::Delete()  
{  
    cout << "客户要求删除一个页面..." << endl;  
}  
  
void CPageGroup::Change()  
{  
    cout << "客户要求修改一个页面..." << endl;  
}  
  
void CPageGroup::Plan()  
{  
    cout << "客户要求页面变更计划..." << endl;  
}  

代码组:CCodePage
CCodePage.h


#ifndef __Command__CodeGroup__  
#define __Command__CodeGroup__  
  
#include <iostream>  
#include "IGroup.h"  
  
class CCodeGroup :public IGroup  
{  
public:  
    CCodeGroup(void);  
    ~CCodeGroup(void);  
    void Find();  
    void Add();  
    void Delete();  
    void Change();  
    void Plan();  
};  

CCodePage.cpp


#include "CodeGroup.h"  
using std::cout;  
using std::endl;  
  
CCodeGroup::CCodeGroup(void)  
{  
}  
  
CCodeGroup::~CCodeGroup(void)  
{  
}  
  
void CCodeGroup::Find()  
{  
    cout << "找到代码组..." << endl;  
}  
  
void CCodeGroup::Add()  
{  
    cout << "客户要求增加一项功能..." << endl;  
}  
  
void CCodeGroup::Delete()  
{  
    cout << "客户要求删除一项功能..." << endl;  
}  
  
void CCodeGroup::Change()  
{  
    cout << "客户要求修改一项功能..." << endl;  
}  
  
void CCodeGroup::Plan()  
{  
    cout << "客户要求代码变更计划..." << endl;  
}  

客户 :main
main.cpp


#include <iostream>  
#include "IGroup.h"  
#include "ICommand.h"  
#include "RequirementGroup.h"  
#include "PageGroup.h"  
#include "CodeGroup.h"  
#include "AddRequirementCommand.h"  
#include "DeletePageCommand.h"  
#include "Invoker.h"  
using std::cout;  
using std::endl;  
  
void DoIt()  
{  
    cout << "----------客户想增加一个需求----------" << endl;  
    IGroup *rg = new CRequirementGroup();  
    rg->Find();  
    rg->Add();  
    rg->Plan();  
    delete rg;  
    cout << endl;  
      
    cout << "----------客户又想修改一个页面----------" << endl;  
    IGroup *pg = new CPageGroup();  
    pg->Find();  
    pg->Add();  
    pg->Plan();  
    delete pg;  
    cout << endl;  
      
    cout << "----------客户又想删除一个功能----------" << endl;  
    IGroup *cg = new CCodeGroup();  
    cg->Find();  
    cg->Add();  
    cg->Plan();  
    delete cg;  
    cout << endl;  
}  
  
void DoNew()  
{  
    cout << "----------客户觉得烦了,希望只找一个人,并告诉他要做什么----------" << endl;  
    cout << "----------客户要求增加一项需求----------" << endl;  
    CInvoker gary;  
    ICommand *pcommand = new CAddRequirementCommand();  
    gary.SetCommand(pcommand);  
    gary.Action();  
    delete pcommand;  
    cout << endl;  
      
    //客户想要改动只需要找CInvoker就可以了。  
    cout << "----------客户要求删除一个页面----------" << endl;  
    CInvoker ricky;  
    ICommand *pcommand2 = new CDeletePageCommand();  
    ricky.SetCommand(pcommand2);  
    ricky.Action();  
    delete pcommand2;  
    cout << endl;  
}  
  
  
int main(int argc, const char * argv[])  
{  
    //客户原来的运行流程  
    cout<<"客户原来的运行流程"<<endl;  
    DoIt();  
      
    //客户觉得麻烦了,每次改动都要找不同的组,谈不同的事  
    //客户只想找一个人,告诉他要做什么就可以,不想关心由哪几个组来做和怎么做  
    cout<<"********************命令模式********************"<<endl;  
    DoNew();  
    // insert code here...  
    std::cout << "Hello, World!\n";  
    return 0;  
}  

结果如下:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容