首页 > 代码库 > C++设计模式之状态模式(一)

C++设计模式之状态模式(一)

    在阐述状态模式之前,先来看一个例子。一个银行取款问题: 如果账户余额大于0,则正常取款;如果余额在-2000和0之间,则透支取款;如果余额小于-2000,则账户处于冻结状态,无法进行取款操作。

    实现代码如下:

//银行账户
class Account
{
private:
	//余额
	int m_nBalance;		
public:
	//取款操作
	void WithDraw()
	{
		if( m_nBalance > 0 )					
		{
			cout << "正常取款状态" << endl;
		}
		else if( m_nBalance > -2000 )			
		{
			cout << "透支状态" << endl;
		}
		else									
		{
			cout << "冻结状态" << endl;
		}
	}
};
    执行取款这一操作,存在正常状态、透支状态、冻结状态三种状态。不同状态下,取款操作对应有不同的行为。如果需要添加一种新的状态,如:账户余额小于-10000,直接注销此账户,并且接受法院的传票,得修改上述代码。执行某一个操作,需要判断这一操作是在哪一个状态下执行的,判断该状态下是否具有该方法,以及特定状态下如何实现该方法,将导致大量的if...else条件判断,新增新的状态,得修改源代码,违背"开放封闭原则"。

    因此有必要对这些状态进行封装,将状态的行为封装到具体的状态中。为了解决这些问题,我们可以使用状态模式,在状态模式中,我们将对象在每一个状态下的行为和状态转移语句封装在一个个状态类中,通过这些状态类来分散冗长的条件转移语句,让系统具有更好的灵活性和可扩展性。

1、状态模式概述

    状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。

状态模式(State Pattern)允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

       在状态模式中引入了抽象状态类和具体状态类,它们是状态模式的核心。

状态模式结构图

    在状态模式结构图中包含如下几个角色:

    Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。

    State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。

    ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

    在状态模式中,我们将对象在不同状态下的行为封装到不同的状态类中,为了让系统具有更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,我们需要对状态进行抽象,引入了抽象状态类角色,其典型代码如下所示:

class State
{
public:
    //声明抽象业务方法,不同的具体状态类可以不同的实现
    void handle();
};

    在抽象状态类的子类即具体状态类中实现了在抽象状态类中声明的业务方法,不同的具体状态类可以提供完全不同的方法实现,在实际使用时,在一个状态类中可能包含多个业务方法,如果在具体状态类中某些业务方法的实现完全相同,可以将这些方法移至抽象状态类,实现代码的复用,典型的具体状态类代码如下所示

class ConcreteState : public State
{
public:
    void handle()
    {
	//方法具体实现代码
    }
}
    环境类维持一个对抽象状态类的引用,通过setState()方法可以向环境类注入不同的状态对象,再在环境类的业务方法中调用状态对象的方法,典型代码如下所示。
class Context
{
private:
	//维持一个对抽象状态对象的引用
    <span style="white-space:pre">	</span>State state; 
	
	//其他属性值,该属性值的变化可能会导致对象状态发生变化
	int value; 
public:
    //设置状态对象
	void setState(State state)
	{
	    this.state = state;
	}

	//调用状态对象的业务方法
	void request()
	{
	    state.handle(); 
    <span style="white-space:pre">	</span>    //其他代码
	}
}
    环境类实际上是真正拥有状态的对象,我们只是将环境类中与状态有关的代码提取出来封装到专门的状态类中,环境类Context与抽象状态类State之间是一种关联关系。和策略模式相似,策略模式封装的是一个个具体策略,环境类Context引用一个具体的策略;而状态模式封装的是一个个具体的状态,环境类Context也维持了一个对具体状态的引用。两者都是通过组合的方式,实现软件复用的功能。

C++设计模式之状态模式(一)