20、状态模式(State)
使用场景:
行为随状态改变而改变的场景。条件、分支语句的代替者。
优点:
a) 满足“单一职责原则”,结构清晰,将与特定状态相关行为局部化到一个状态中,并且将不同状态的行为分割开来
b) 将状态转换显示,减少对象间相互依赖。将不同状态引入独立对象中使状态更加明确,且减少对象间相互依赖
c) 状态类职责明确,有利于程序的扩展,定义新子类容易地增加新状态和转换
缺点:
a) 会增加系统类和对象的个数。
b) 结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
c) 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,
模式结构:
a) 环境类角色(Context):也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
b) 抽象状态角色(State):定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
c) 具体状态角色(Concrete State):实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
state.h
#ifndef _STATE_H_
#define _STATE_H_
class Context;
class State {
public:
virtual void Handle(Context* pContext) = 0;
~State();
protected:
State();
};
class ConcreteStateA : public State {
public:
ConcreteStateA();
~ConcreteStateA();
virtual void Handle(Context* pContext);
};
class ConcreteStateB : public State {
public:
ConcreteStateB();
~ConcreteStateB();
virtual void Handle(Context* pContext);
};
class ConcreteStateC : public State {
public:
ConcreteStateC();
~ConcreteStateC();
virtual void Handle(Context* pContext);
};
class Context {
public:
Context(State* pState);
~Context();
void Request();
void ChangeState(State* pState);
private:
State* _state;
};
#endif _STATE_H_
state.cpp
#include "stdafx.h"
#include <iostream>
#include <string>
#include "state.h"
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
int main() {
State* pState = new ConcreteStateA();
Context* pContext = new Context(pState);
pContext->Request();
pContext->Request();
pContext->Request();
pContext->Request();
pContext->Request();
return 0;
}
运行结果:
状态模式的扩展
在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享
状态模式与责任链模式的区别:
a) 状态模式和责任链模式都能消除 if-else 分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用。
b) 从定义来看,状态模式强调的是一个对象内在状态的改变,而责任链模式强调的是外部节点对象间的改变。
c) 从代码实现上来看,两者最大的区别就是状态模式的各个状态对象知道自己要进入的下一个状态对象,而责任链模式并不清楚其下一个节点处理对象,因为链式组装由客户端负责。
状态模式与策略模式的区别
状态模式和策略模式的 UML 类图架构几乎完全一样,但两者的应用场景是不一样的。策略模式的多种算法行为择其一都能满足,彼此之间是独立的,用户可自行更换策略算法,而状态模式的各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户无法指定状态,只能设置初始状态。