State模式详解--设计模式(14)

State模式来源: 

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

有限状态自动机(FSM)也是一个典型的状态不同,对输入有不同的响应(状态转移)。通常我们在实现这类系统会使用到很多的Switch/Case语句,Case某种状态,发生什么动作,Case另外一种状态,则发生另外一种状态。但是这种实现方式至少有以下两个问题:

(1).当状态数目不是很多的时候,Switch/Case可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组的Switch/Case语句将是一件异常困难并且容易出错的事情。

(2).状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。

State模式作用:

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

State模式UML结构图如图1所示:

 
                   

State模式的构成:

State类:抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为。

ConcreteState类:具体状态,每一个子类实现一个与Context的一个状态相关的行为。

Context类:维护一个ConcreteState子类的实例,这个实例定义当前的状态。

State模式的代码示例:

State.h

#ifndef _STATE_H_
#define _STATE_H_

class Context;
class State
{
public:
    virtual void Handle(Context* pContext)=0;
    ~State();
protected:
    State();
private:
};

class ConcreteStateA : public State
{
public:
    ConcreteStateA();
    ~ConcreteStateA();
    virtual void Handle(Context* pContext);
protected:
private:
};

class ConcreteStateB : public State
{
public:
    ConcreteStateB();
    ~ConcreteStateB();
    virtual void Handle(Context* pContext);
protected:
private:
};

class ConcreteStateC : public State
{
public:
    ConcreteStateC();
    ~ConcreteStateC();
    virtual void Handle(Context* pContext);
protected:
private:
};

class Context
{
public:
    Context(State* pState);
    ~Context();
    void Request();
    void ChangeState(State* pState);
protected:
private:
    State* _state;
};

#endif

State.cpp

#include "State.h"
#include <iostream>
using namespace std;
State::State()
{}
State::~State()
{}
ConcreteStateA::ConcreteStateA()
{}
ConcreteStateA::~ConcreteStateA()
{}
//执行该状态的行为并改变状态
void ConcreteStateA::Handle(Context* pContext)
{
    cout << "ConcreteStateA" << endl;
    pContext->ChangeState(new ConcreteStateB());
}
ConcreteStateB::ConcreteStateB()
{}
ConcreteStateB::~ConcreteStateB()
{}
//执行该状态的行为并改变状态
void ConcreteStateB::Handle(Context* pContext)
{
    cout << "ConcreteStateB" << endl;
    pContext->ChangeState(new ConcreteStateC());
}
ConcreteStateC::ConcreteStateC()
{}
ConcreteStateC::~ConcreteStateC()
{}
//执行该状态的行为并改变状态
void ConcreteStateC::Handle(Context* pContext)
{
    cout << "ConcreteStateC" << endl;
    pContext->ChangeState(new ConcreteStateA());
}

//定义_state的初始状态
Context::Context(State* pState)
{
    this->_state = pState;
}
Context::~Context()
{}
//对请求做处理,并设置下一状态
void Context::Request()
{
    if(NULL != this->_state)
    {
        this->_state->Handle(this);
    }
}
//改变状态
void Context::ChangeState(State* pState)
{
    this->_state = pState;
}

Main.cpp

#include "State.h"

int main()
{
    State* pState = new ConcreteStateA();
    Context* pContext = new Context(pState);
    pContext->Request();
    pContext->Request();
    pContext->Request();
    pContext->Request();
    pContext->Request();
    return 0;
}

State模式使用场景:

 
     
State模式的应用也非常广泛,从最高层逻辑用户接口GUI到最底层的通讯协议(例如利用State模式模拟实现一个TCP连接的类。)都有其用武之地。

State模式和Strategy模式又很大程度上的相似:它们都有一个Context类,都是通过委托(组合)给一个具有多个派生类的多态基类实现Context的算法逻辑。两者最大的差别就是State模式中派生类持有指向Context对象的引用,并通过这个引用调用Context中的方法,但在Strategy模式中就没有这种情况。因此可以说一个State实例同样是Strategy模式的一个实例,反之却不成立。实际上State模式和Strategy模式的区别还在于它们所关注的点不尽相同:State模式主要是要适应对象对于状态改变时的不同处理策略的实现,而Strategy则主要是具体算法和实现接口的解耦(coupling),Strategy模式中并没有状态的概念(虽然很多时候有可以被看作是状态的概念),并且更加不关心状态的改变了。

State模式很好地实现了对象的状态逻辑和动作实现的分离,状态逻辑分布在State的派生类中实现,而动作实现则可以放在Context类中实现(这也是为什么State派生类需要拥有一个指向Context的指针)。这使得两者的变化相互独立,改变State的状态逻辑可以很容易复用Context的动作,也可以在不影响State派生类的前提下创建Context的子类来更改或替换动作实现。

State模式经典示例:

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

1->main(),客户

2->CLiftState,电梯状态抽象类

3->CCloseingState,电梯门关闭

4->COpenningState,电梯门打开

5->CRunningState,电梯运行

6->CStoppingState,电梯停止

7->CContext,电梯的控制面板

说明:CContext保持电梯的状态,并提供操作的接口函数。当函数被调用时,CContext直接调用当前状态的相应函数。由状态的接口函数来确定是否可以执行这个动作,以及修改状态为执行这个动作后的状态。

看代码:第一块是不使用模式的做法,第二块是使用模式的做法,在main()函数里会有调用的方式。

<span style="font-size:14px;"><span style="font-size:14px;">//ILift.h
#pragma once
class ILift
{
public:

    ILift(void)
    {
    }

    virtual ~ILift(void)
    {
    }
    static const int OPENING_STATE = 1;
    static const int CLOSING_STATE = 2;
    static const int RUNNING_STATE = 3;
    static const int STOPPING_STATE = 4;
    virtual void SetState(int state) = 0;
    virtual void Open() = 0;
    virtual void Close() = 0;
    virtual void Run() = 0;
    virtual void Stop() = 0;
};

//Lift.h
#pragma once
#include "ilift.h"
class CLift :
    public ILift
{
public:
    CLift(void);
    ~CLift(void);
    void SetState(int state);
    void Open();
    void Close();
    void Run();
    void Stop();
private:
    int m_state;
    void OpenWithoutLogic();
    void CloseWithoutLogic();
    void RunWithoutLogic();
    void StopWithoutLogic();
};

//Lift.cpp
#include "StdAfx.h"
#include "Lift.h"
#include <iostream>
using std::cout;
using std::endl;

CLift::CLift(void)
{
    this->m_state = 0;
}

CLift::~CLift(void)
{
}

void CLift::SetState(int state)
{
    this->m_state = state;
}

void CLift::Open()
{
    switch(this->m_state)
    {
    case OPENING_STATE:
        break;
    case CLOSING_STATE:
        this->OpenWithoutLogic();
        this->SetState(OPENING_STATE);
        break;
    case RUNNING_STATE:
        break;
    case STOPPING_STATE:
        this->OpenWithoutLogic();
        this->SetState(OPENING_STATE);
        break;
    }
}

void CLift::Close()
{
    switch(this->m_state)
    {
    case OPENING_STATE:
        this->CloseWithoutLogic();
        this->SetState(CLOSING_STATE);
        break;
    case CLOSING_STATE:
        break;
    case RUNNING_STATE:
        break;
    case STOPPING_STATE:
        break;
    }
}

void CLift::Run()
{
    switch(this->m_state)
    {
    case OPENING_STATE:
        break;
    case CLOSING_STATE:
        this->RunWithoutLogic();
        this->SetState(RUNNING_STATE);
        break;
    case RUNNING_STATE:
        break;
    case STOPPING_STATE:
        this->RunWithoutLogic();
        this->SetState(RUNNING_STATE);
        break;
    }
}

void CLift::Stop()
{
    switch(this->m_state)
    {
    case OPENING_STATE:
        break;
    case CLOSING_STATE:
        this->StopWithoutLogic();
        this->SetState(CLOSING_STATE);
        break;
    case RUNNING_STATE:
        this->StopWithoutLogic();
        this->SetState(CLOSING_STATE);
        break;
    case STOPPING_STATE:
        break;
    }
}

void CLift::OpenWithoutLogic()
{
    cout << "电梯门开启..." << endl;
}

void CLift::CloseWithoutLogic()
{
    cout << "电梯门关闭..." << endl;
}

void CLift::RunWithoutLogic()
{
    cout << "电梯上下跑起来..." << endl;
}

void CLift::StopWithoutLogic()
{
    cout << "电梯停止了..." << endl;
}</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//LiftState.h
#pragma once
class CContext;
class CLiftState
{
public:
    CLiftState(void);
    virtual ~CLiftState(void);
    void SetContext(CContext *pContext);
    virtual void Open() = 0;
    virtual void Close() = 0;
    virtual void Run() = 0;
    virtual void Stop() = 0;
protected:
    CContext *m_pContext;
};

//LiftState.cpp
#include "StdAfx.h"
#include "LiftState.h"

CLiftState::CLiftState(void)
{
}

CLiftState::~CLiftState(void)
{
}

void CLiftState::SetContext( CContext *pContext )
{
    m_pContext = pContext;
}

//CloseingState.h
#pragma once
#include "liftstate.h"
class CCloseingState :
    public CLiftState
{
public:
    CCloseingState(void);
    ~CCloseingState(void);
    void Open();
    void Close();
    void Run();
    void Stop();
};

//CloseingState.cpp
#include "StdAfx.h"
#include "CloseingState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;

CCloseingState::CCloseingState(void)
{
}

CCloseingState::~CCloseingState(void)
{
}

void CCloseingState::Open()
{
    this->CLiftState::m_pContext->SetLiftState(CContext::pOpenningState);
    this->CLiftState::m_pContext->GetLiftState()->Open();
}

void CCloseingState::Close()
{
    cout << "电梯门关闭..." << endl;
}

void CCloseingState::Run()
{
    this->CLiftState::m_pContext->SetLiftState(CContext::pRunningState);
    this->CLiftState::m_pContext->GetLiftState()->Run();
}

void CCloseingState::Stop()
{
    this->CLiftState::m_pContext->SetLiftState(CContext::pStoppingState);
    this->CLiftState::m_pContext->GetLiftState()->Stop();
}

//OpenningState.h
#pragma once
#include "liftstate.h"
class COpenningState :
    public CLiftState
{
public:
    COpenningState(void);
    ~COpenningState(void);
    void Open();
    void Close();
    void Run();
    void Stop();
};

//OpenningState.cpp
#include "StdAfx.h"
#include "OpenningState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;

COpenningState::COpenningState(void)
{
}

COpenningState::~COpenningState(void)
{
}

void COpenningState::Open()
{
    cout << "电梯门开启..." << endl;
}

void COpenningState::Close()
{
    this->CLiftState::m_pContext->SetLiftState(CContext::pCloseingState);
    this->CLiftState::m_pContext->GetLiftState()->Close();
}

void COpenningState::Run()
{
    //do nothing
}

void COpenningState::Stop()
{
    //do nothing
}

//RunningState.h
#pragma once
#include "liftstate.h"
class CRunningState :
    public CLiftState
{
public:
    CRunningState(void);
    ~CRunningState(void);
    void Open();
    void Close();
    void Run();
    void Stop();
};

//RunningState.cpp
#include "StdAfx.h"
#include "RunningState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;

CRunningState::CRunningState(void)
{
}

CRunningState::~CRunningState(void)
{
}

void CRunningState::Open()
{
    //do nothing
}

void CRunningState::Close()
{
    //do nothing
}

void CRunningState::Run()
{
    cout << "电梯上下跑..." << endl;
}

void CRunningState::Stop()
{
    this->CLiftState::m_pContext->SetLiftState(CContext::pStoppingState);
    this->CLiftState::m_pContext->GetLiftState()->Stop();
}

//StoppingState.h
#pragma once
#include "liftstate.h"
class CStoppingState :
    public CLiftState
{
public:
    CStoppingState(void);
    ~CStoppingState(void);
    void Open();
    void Close();
    void Run();
    void Stop();
};

//StoppingState.cpp
#include "StdAfx.h"
#include "StoppingState.h"
#include "Context.h"
#include <iostream>
using std::cout;
using std::endl;

CStoppingState::CStoppingState(void)
{
}

CStoppingState::~CStoppingState(void)
{
}

void CStoppingState::Open()
{
    this->CLiftState::m_pContext->SetLiftState(CContext::pOpenningState);
    this->CLiftState::m_pContext->GetLiftState()->Open();
}

void CStoppingState::Close()
{
    //do nothing
}

void CStoppingState::Run()
{
    this->CLiftState::m_pContext->SetLiftState(CContext::pRunningState);
    this->CLiftState::m_pContext->GetLiftState()->Run();
}

void CStoppingState::Stop()
{
    cout << "电梯停止了..." << endl;
}</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//Contex.h
#pragma once
#include "LiftState.h"
#include "OpenningState.h"
#include "CloseingState.h"
#include "RunningState.h"
#include "StoppingState.h"
class CContext
{
public:
    CContext(void);
    ~CContext(void);
    static COpenningState *pOpenningState;
    static CCloseingState *pCloseingState;
    static CRunningState *pRunningState;
    static CStoppingState *pStoppingState;
    CLiftState * GetLiftState();
    void SetLiftState(CLiftState *pLiftState);
    void Open();
    void Close();
    void Run();
    void Stop();
private:
    CLiftState *m_pLiftState;
};

//Context.cpp
#include "StdAfx.h"
#include "Context.h"
COpenningState* CContext::pOpenningState = NULL;
CCloseingState* CContext::pCloseingState = NULL;
CRunningState* CContext::pRunningState = NULL;
CStoppingState* CContext::pStoppingState = NULL;

CContext::CContext(void)
{
    m_pLiftState = NULL;
    pOpenningState = new COpenningState();
    pCloseingState = new CCloseingState();
    pRunningState = new CRunningState();
    pStoppingState = new CStoppingState();
}

CContext::~CContext(void)
{
    delete pOpenningState;
    pOpenningState = NULL;
    delete pCloseingState;
    pCloseingState = NULL;
    delete pRunningState;
    pRunningState = NULL;
    delete pStoppingState;
    pStoppingState = NULL;
}

CLiftState * CContext::GetLiftState()
{
    return m_pLiftState;
}

void CContext::SetLiftState(CLiftState *pLiftState)
{
    this->m_pLiftState = pLiftState;
    this->m_pLiftState->SetContext(this);
}

void CContext::Open()
{
    this->m_pLiftState->Open();
}

void CContext::Close()
{
    this->m_pLiftState->Close();
}

void CContext::Run()
{
    this->m_pLiftState->Run();
}

void CContext::Stop()
{
    this->m_pLiftState->Stop();
}

// State.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "ILift.h"
#include "Lift.h"
#include "Context.h"
#include "OpenningState.h"
#include "CloseingState.h"
#include "RunningState.h"
#include "StoppingState.h"
#include <iostream>
using std::cout;
using std::endl;

void DoIt()
{
    //ILift.h, Lift.h, Lift.cpp
    ILift *pLift = new CLift();
    pLift->SetState(ILift::STOPPING_STATE);//电梯的初始条件是停止状态。
    pLift->Open();//首先是电梯门开启,人进去
    pLift->Close();//然后电梯门关闭
    pLift->Run();//再然后,电梯跑起来,向上或者向下
    pLift->Stop();//最后到达目的地,电梯停下来
    delete pLift;
}

void DoNew()
{
    //LiftState.h, LiftState.cpp, OpenningState.h, CloseingState.h, RunningState.h, StoppingState.h
    //Context.h, Context.cpp
    CContext context;
    CCloseingState closeingState;
    context.SetLiftState(&closeingState);
    context.Close();
    context.Open();
    context.Run();
    context.Stop();
}

int _tmain(int argc, _TCHAR* argv[])
{
    cout << "----------使用模式之前----------" << endl;
    DoIt();
    cout << "----------使用模式之后----------" << endl;
    DoNew();

    _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF);
    _CrtDumpMemoryLeaks();
    return 0;
}</span></span>

State状态模式的优点:

(1).状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。

(2).所有状态相关的代码都存在于某个ConcereteState中,所以通过定义新的子类很容易地增加新的状态和转换。

(3).状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖。

State状态模式的缺点:

(1).在于使用状态模式会增加系统类和对象的个数,且状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,对于可以切换状态的状态模式不满足“开闭原则”的要求。导致较多的ConcreteState子类

State状态模式使用总结:

策略模式关注行为的变化,但归根结底只有一个行为,变化的只是行为的实现.客户不关注这些.当新增变化时对客户可以没有任何影响.状态模式同样关注行为的变化,但这个变化是由状态来驱动,一般来说每个状态和行为都不同.新增的状态或行为一般与已有的不同,客户需要关注这些变化.状态模式中State及其子类中的操作都将Context传入作为参数,以便State可以通过这个指针调用Context中的方法,修改状态。而策略模式没有。

时间: 2024-10-13 10:55:38

State模式详解--设计模式(14)的相关文章

Strategy模式详解--设计模式(13)

Strategy模式来源:        在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法:当然也可以将这些查找算法封装在一个统一的方法中,通过if-else-或者case等条件判断语句来进行选择.这两种实现方法我们都可以称之为硬编

Observer模式详解--设计模式(15)

Observer模式来源: Observer模式应该可以说是应用最多.影响最广的模式之一. 因为Observer的一个实例Model/View/Control(MVC)结构在系统开发架构设计中有着很重要的地位和意义,MVC实现了业务逻辑和表示层的解耦.在MFC中,Doc/View(文档视图结构)提供了实现MVC的框架结构(有一个从设计模式(Observer模式)的角度分析分析Doc/View的文章正在进一步的撰写当中,遗憾的是时间:)).在Java阵容中,Struts则提供和MFC中Doc/Vi

Proxy模式详解--设计模式(11)

Proxy模式的产生原因:        在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 当由于某些特定的需要调用的对象在另外一台机器上,需要跨越网络才能访问,在没有WebService的情况下我们需要直接coding去处理网络连接.处理打包.解包等等非常复杂的步骤,而WebService的出现帮我们解决了其中的一些问题简化客户端的处理,我们只需在客户端建立一个远程对象的代理,客户端就象调用本地对象一样调用该代理,再由代理去跟实际对象联

Interpreter 模式详解--设计模式(22)

Interpreter 模式的来源: Interpreter(解释器)模式是一种特殊的设计模式,它建立一个解释器(Interpreter),对于特定的计算机程序设计语言,用来解释预先定义的文法.简单地说,Interpreter模式是一种简单的语法解释器构架.解释器模式属于行为模式,给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. Interpreter 模式作用:     正如其名,此模式大多用来解释一些(自定义的)独特语法,例如某些游戏开发引擎中

Memento模式详解--设计模式(16)

Memento模式来源:      我们在进行软件系统的设计时候是要给用户后悔的权利(实际上可能也是用户要求的权利:)),我们对一些关键性的操作肯定需要提供诸如撤销(Undo)的操作.那这个后悔药就是Memento模式提供的. Memento模式作用:       在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态. Memento模式UML结构图如图1所示:                       Memento模式构成:

Flyweight模式详解--设计模式(9)

Flyweight模式产生原因: 在面向对象系统的设计何实现中,创建对象是最为常见的操作.这里面就有一个问题:如果一个应用程序使用了太多的对象,就会造成很大的存储开销.特别是对于大量轻量级(细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费.例如一个字母"a"在文档中出现了100000次,而实际上我们可以让这一万个字母"a"共享一个对象,当然因为在不同的位置可能字母"a"

C++Builder建造者模式详解--设计模式(4)

生活中有着很多的Builder的例子,个人觉得大学生活就是一个Builder模式的最好体验:要完成大学教育,一般将大学教育过程分成4个学期进行,因此没有学习可以看作是构建完整大学教育的一个部分构建过程,每个人经过这4年的(4个阶段)构建过程得到的最后的结果不一样,因为可能在四个阶段的构建中引入了很多的参数(每个人的机会和际遇不完全相同). Builder模式要解决的也正是这样的问题:当我们要创建的对象很复杂的时候(通常是由很多其他的对象组合而成),我们要要复杂对象的创建过程和这个对象的表示(展示

Template模式详解--设计模式(12)

Template模式来源:    在面向对象系统的分析与设计过程中经常会遇到这样一种情况:对于某一个业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用算法)是相同的.Template提供了这种情况的一个实现框架. Template模式作用: Template模式又叫模板方法模式,在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情冴下,重新定义算法中的某些步骤. 我们使用冲泡咖啡和冲泡茶的例子 加工流程: 咖啡冲

Iterator模式详解--设计模式(21)

Iterator模式来源: 迭代器(Iterator)模式,又叫做游标(Cursor)模式.GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节.从定义可见,迭代器模式是为容器而生. Iterator模式作用: (1).它支持以不同的方式遍历一个聚合复杂的聚合可用多种方式进行遍历,如二叉树的遍历,可以采用前序.中序或后序遍历.迭代器模式使得改变遍历算法变得很容易: 仅需用一个不同的迭代器的实例代替原先的实例即可,你也可以自己定义迭代器的子