Published on

设计模式(20)——状态 State

Authors
  • avatar
    Name
    Leon
    Twitter

二十、State(状态模式,别名 Objects for States 状态对象,对象行为型模式)

1. 意图:

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

2. 适用:

  1. 一个对象的行迹取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
  2. 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State 模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

3. 类图:

4. State 与 Strategy

  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 模式问题主要是逻辑分散化,状态逻辑分布到了很多的 State 的子类中,很难看到整个的状态逻辑图,这也带来了代码的维护问题。

5. 思考:

  不能说状态模式一定是好的,只能说当行为 action 的数量是比较固定的情况下,使用状态模式才是好的。实现一个状态机有两种方式,第一种是将行为 action 封装起来,在行为函数中处理每一种状态。第二种方式是将状态 state 封装,在状态 state 中处理每一种行为 action。状态模式就是第二种方式。它将状态 state 封装成一个对象,在这个对象中处理每一种行为 action。这种方式添加状态很方便,只需要修改与新增状态有关的几个状态对象就可以了。但是这种方式添加行为很麻烦需要对所以状态对象做修改,来处理这种行为。所以说状态模式适用于行为 action 变动比较小,而状态变动比较频繁的情况。

6. C++实现:

  1. 编写一个环境类 Context, has-a 有一个 具体的 State 对象如 ConcreteStateA 对象 _state
  2. 编写 Context 的操作行为函数 OperationChangeState(),函数体内调用 _state 对象的与特定状态相关的具体行为
  3. 编写一个状态接口 State 以封装与 Context 的一个特定状态相关的行为,如接口 OperationInterface()OperationChangeState()
  4. 编写 State 中一个改变 Context 状态的方法 ChangeState(Context* con, State* st),State 在调用自己的操作行为 OperationChangeState() 时通过调用 ChangeState 方法来改变 Context 的状态,这样,下次 Context 再调用自己的操作函数(Context 的 OperationChangeState)时,就会调用改变后的新状态的操作函数(另一个 State 实现类的对象 的 OperationChangeState),这种执行操作行为后改变 Context 状态的机制就形成了状态机模式。
  5. Context 为了封装,其状态 _state 是私有的,所以为了让 State 实现类能够访问(改变)Context 状态,在 Context 中将 State 声明为其友元类 friend class State;

State.h

//State.h
#pragma once

class Context;

class State {
public:
	State();
	virtual ~State();
	virtual void OperationInterface(Context*) = 0;
	virtual void OperationChangeState(Context*) = 0;
	bool ChangeState(Context* con, State* st);
private:
};

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

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

State.cpp

//State.cpp
#include "State.h"
#include "Context.h"

#include <iostream>

using namespace::std;

State::State() {}
State::~State(){}
void State::OperationInterface(Context* con) {
	cout << "State::..." << endl;
}
bool State::ChangeState(Context* con, State* st) {
	con->ChangeState(st);
	return true;
}
void State::OperationChangeState(Context* con) {}

ConcreteStateA::ConcreteStateA() {}
ConcreteStateA::~ConcreteStateA() {}

void ConcreteStateA::OperationInterface(Context* con) {
	cout << "ConcreteStateA::OperationInterface......" << endl;
}
void ConcreteStateA::OperationChangeState(Context* con) {
	OperationInterface(con);
	this->ChangeState(con, new ConcreteStateB());
}
ConcreteStateB::ConcreteStateB() {}
ConcreteStateB::~ConcreteStateB() {}
void ConcreteStateB::OperationInterface(Context*  con) {
	cout << "ConcreteStateB::OperationInterface..." << endl;
}
void ConcreteStateB::OperationChangeState(Context* con) {
	OperationInterface(con);
	this->ChangeState(con, new ConcreteStateA());
}

Context.h

//Context.h
#pragma once

class State;

class Context {
public:
	Context();
	Context(State* state);
	~Context();
	void OperationInterface();
	void OperationChangeState();
protected:
	friend class State;	// 表明在 State 类中可以访问 Context 类的 private 字段
	bool ChangeState(State* state);
private:
	State* _state;
};

Context.cpp

//Context.cpp
#include "Context.h"
#include "State.h"

#include <iostream>
using namespace::std;

Context::Context(State* state) {
	this->_state = state;
}
Context::~Context() {
	delete _state;
}
void Context::OperationInterface() {
	_state->OperationInterface(this);
}
bool Context::ChangeState(State* state) {
	this->_state = state;
	return true;
}
void Context::OperationChangeState() {
	_state->OperationChangeState(this);
}

main.cpp

//main.cpp
#include "Context.h"
#include "State.h"

#include <iostream>
using namespace::std;

int main(int argc, char* argv[]) {
	State* st = new ConcreteStateA();
	Context* con = new Context(st);
	con->OperationChangeState();
	con->OperationChangeState();
	con->OperationChangeState();
	con->OperationChangeState();
	if(con != NULL)
		delete con;
	if(st != NULL)
		st = NULL;
	return 0;
}