1.概述
状态模式是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。
2.结构
- State(抽象状态类):定义一个接口用来封装与上下文类的一个特定状态相关的行为,可以有一个或多个行为。
- ConcreteState(具体状态类):作为抽象状态类的子类,每个子类实现一个与上下文类的一个状态相关的行为。每个具体状态类对应上下文类的一个具体状态,不同的具体状态类其行为有所不同。
- Context(上下文类):上下文类中维护一个具体状态的实例,通常有多种状态,负责具体状态的切换。
3.实现
3.1 实例类比
在日常生活和工作开发中都有状态的问题。你如说马路上的红绿灯,这三个灯光就属于三种状态。再比如说我使用的文档系统,通常有草稿、审阅和批发部三种状态。
在以往的概念里,我们都会使用if...esle这类语句进行设计,但是这种设计一旦后期的需求发生了改变,那么维护起来相当的复杂!
一个 文档
Document类。 文档可能会处于 草稿
Draft 、 审阅中
Moderation和 已发布
Published三种状态中的一种。 文档的 publish
发布方法在不同状态下的行为略有不同:
- 处于
草稿
状态时, 它会将文档转移到审阅中状态。 - 处于
审阅中
状态时, 如果当前用户是管理员, 它会公开发布文档。 - 处于
已发布
状态时, 它不会进行任何操作。
3.2 具体实现
#include <iostream>
#include <typeinfo>
class Context;
//抽象状态类
class State {
protected:
Context *context_;
public:
virtual ~State() {
}
void set_context(Context *context) {
this->context_ = context;
}
virtual void Status() = 0;
virtual void Handle() = 0;
};
//上下文类
class Context {
private:
State *state_;
public:
Context(State *state) : state_(nullptr) {
this->TransitionTo(state);
}
~Context() {
delete state_;
}
void TransitionTo(State *state) {
std::cout << "Context: Transition to " << typeid(*state).name() << ".\n";
if (this->state_ != nullptr)
delete this->state_;
this->state_ = state;
this->state_->set_context(this);
}
void Request1() {
this->state_->Status();
}
void Request2() {
this->state_->Handle();
}
};
//已发布状态
class PublishedState : public State {
public:
void Status() override {
std::cout << "处于已发布状态.\n";
}
void Handle() override {
std::cout << "结束...\n";
}
};
//审阅中状态
class ModerationState : public State {
public:
void Status() override {
std::cout << "处于审阅中状态.\n";
};
void Handle() override {
this->context_->TransitionTo(new PublishedState);
}
};
//草稿状态
class DraftState : public State {
public:
void Status() override {
std::cout << "处于草稿状态.\n";
};
void Handle() override {
this->context_->TransitionTo(new ModerationState);
}
};
/**
* The client code.
*/
void ClientCode() {
Context *context = new Context(new DraftState);
context->Request1();
context->Request2();
//
context->Request1();
context->Request2();
//
context->Request1();
context->Request2();
delete context;
}
int main() {
ClientCode();
return 0;
}
3.3运行结果
4.状态设计模式优缺点
优点:
通常对有状态的对象进行编程,我们的解决方案是:思考可能存在的所有状态,然后使用 if-else 或 switch-case 语句来进行状态判断,然后再根据不同的状态进行不同的处理。
大量的if...else的缺点很明显
- 违背开闭原则: 当增加一种状态的时候, 需要修改原来的逻辑
- 当状态很多的时候, 代码段很长, 臃肿, 不容易维护, 可扩展性差.
状态模式可以很好地解决这个问题。封装了转换规则,消除了 if-else、switch-case 等条件判断语句,代码更有层次性,且具备良好的扩展力, 可维护性。
缺点
- 状态模式的使用必然会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
- 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
5.应用场景
- 一个操作中含有庞大的多分支结构,并且这些分支取决于对象的状态。
- 对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化。