一、概述
描述:一天有早中晚,不同时间下,太阳光是不一样的。所以随着早中晚的状态变化,太阳的行为也随着变化。
定义:状态模式是一种行为设计模式, 让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。这其实就有点类似算法中的有限状态机的形式。
其主要思想是程序在任意时刻仅可处于几种有限的状态中。 在任何一个特定状态中, 程序的行为都不相同, 且可瞬间从一个状态切换到另一个状态。 不过, 根据当前状态, 程序可能会切换到另外一种状态, 也可能会保持当前状态不变。 这些数量有限且预先定义的状态切换规则被称为转移。
二、结构
-
上下文(Context)保存了对于一个具体状态对象的引用,并会将所有与该状态相关的工作委派给它。上下文通过状态接口与状态对象交互,且会提供一个设置器用于传递新的状态对象。
-
状态(State)接口会声明特定于状态的方法。这些方法应能被其他所有具体状态所理解,因为你不希望某些状态所拥有的方法永远不会被调用。
-
具体状态(Concrete States)会自行实现特定于状态的方法。为了避免多个状态中包含相似代码,你可以提供一个封装有部分通用行为的中间抽象类。状态对象可存储对于上下文对象的反向引用。状态可以通过该引用从上下文处获取所需信息,并且能触发状态转移。
-
上下文和具体状态都可以设置上下文的下个状态,并可通过替换连接到上下文的状态对象来完成实际的状态转换。
三、实例
#include<iostream>
#include<string>
#include<cstdio>
#include<memory>
using namespace std;
class AbstractState;
// 论坛账号
class ForumAccount {
private:
shared_ptr<AbstractState> state_;
string name_;
public:
explicit ForumAccount(string name);
void set_state(shared_ptr<AbstractState> state) {
state_ = state;
}
shared_ptr<AbstractState> get_state() {
return state_;
}
string get_name() {
return name_;
}
void downloadFile(int score);
void writeNote(int score);
void replyNote(int score);
};
class AbstractState {
protected:
ForumAccount* account_;
int point_;
string state_name_;
public:
virtual void checkState() = 0;
void set_point(int point) {
point_ = point;
}
int get_point() {
return point_;
}
void set_state_name(string name) {
state_name_ = name;
}
string get_state_name() {
return state_name_;
}
ForumAccount* get_account() {
return account_;
}
virtual void downloadFile(int score) {
printf("%s下载文件, 扣除%d积分。\n", account_->get_name().c_str(), score);
point_ -= score;
checkState();
printf("%s剩余积分为%d, 当前级别为%s。\n", account_->get_name().c_str(), point_, account_->get_state()->get_state_name().c_str());
}
virtual void writeNote(int score) {
printf("%s发布留言, 增加%d积分。\n", account_->get_name().c_str(), score);
point_ += score;
checkState();
printf("%s剩余积分为%d, 当前级别为%s。\n", account_->get_name().c_str(), point_, account_->get_state()->get_state_name().c_str());
}
virtual void replyNote(int score) {
printf("%s回复留言, 增加%d积分。\n", account_->get_name().c_str(), score);
point_ += score;
checkState();
printf("%s剩余积分为%d, 当前级别为%s。\n", account_->get_name().c_str(), point_, account_->get_state()->get_state_name().c_str());
}
};
// 具体状态类: 新手
class PrimaryState : public AbstractState {
public:
explicit PrimaryState(AbstractState* state) {
account_ = state->get_account();
point_ = state->get_point();
state_name_ = "新手";
}
explicit PrimaryState(ForumAccount* account) {
account_ = account;
point_ = 0;
state_name_ = "新手";
}
void downloadFile(int score) override {
printf("对不起, %s没有下载文件的权限!\n", account_->get_name().c_str());
}
void checkState() override;
};
// 具体状态类: 高手
class MiddleState : public AbstractState {
public:
explicit MiddleState(AbstractState* state) {
account_ = state->get_account();
point_ = state->get_point();
state_name_ = "高手";
}
void writeNote(int score) override {
printf("%s发布留言, 增加%d积分。\n", account_->get_name().c_str(), score * 2);
point_ += score * 2;
checkState();
printf("%s剩余积分为%d, 当前级别为%s。\n", account_->get_name().c_str(), point_, account_->get_state()->get_state_name().c_str());
}
void checkState() override;
};
// 具体状态类: 专家
class HighState : public AbstractState {
public:
explicit HighState(AbstractState* state) {
account_ = state->get_account();
point_ = state->get_point();
state_name_ = "专家";
}
void writeNote(int score) override {
printf("%s发布留言, 增加%d积分。\n", account_->get_name().c_str(), score * 2);
point_ += score * 2;
checkState();
printf("%s剩余积分为%d, 当前级别为%s。\n", account_->get_name().c_str(), point_, account_->get_state()->get_state_name().c_str());
}
virtual void downloadFile(int score) {
printf("%s下载文件, 扣除%d积分。\n", account_->get_name().c_str(), score / 2);
point_ -= score / 2;
checkState();
printf("%s剩余积分为%d, 当前级别为%s。\n", account_->get_name().c_str(), point_, account_->get_state()->get_state_name().c_str());
}
void checkState() override;
};
void PrimaryState::checkState() {
if (point_ >= 1000) {
account_->set_state(make_shared<HighState>(this));
}
else if (point_ >= 100) {
account_->set_state(make_shared<MiddleState>(this));
}
}
void MiddleState::checkState() {
if (point_ >= 1000) {
account_->set_state(make_shared<HighState>(this));
}
else if (point_ < 100) {
account_->set_state(make_shared<PrimaryState>(this));
}
}
void HighState::checkState() {
if (point_ < 100) {
account_->set_state(make_shared<PrimaryState>(this));
}
else if (point_ < 1000) {
account_->set_state(make_shared<HighState>(this));
}
}
ForumAccount::ForumAccount(std::string name)
: name_(name), state_(make_shared<PrimaryState>(this)) {
printf("账号%s注册成功!\n", name.c_str());
}
void ForumAccount::downloadFile(int score) {
state_->downloadFile(score);
}
void ForumAccount::writeNote(int score) {
state_->writeNote(score);
}
void ForumAccount::replyNote(int score) {
state_->replyNote(score);
}
int main() {
// 注册新用户
ForumAccount account("TOMOCAT");
account.writeNote(20);
account.downloadFile(20);
account.replyNote(100);
account.writeNote(40);
account.downloadFile(80);
account.writeNote(1000);
account.downloadFile(80);
return 0;
}
四、适用场景
- 对象需要根据自身当前状态进行不同行为,同时状态的数量非常多且与状态相关的代码会频繁变更的话,可使用状态模式。
- 某个类需要根据成员变量的当前值改变自身行为,从而需要使用大量的条件语句时,可使用该模式。
- 当相似状态和基于条件的状态机转换中存在许多重复代码时,可使用状态模式。
五、优缺点
优点:
- 单一职责原则。将与特定状态相关的代码放在单独的类中。
- 开闭原则。无需修改已有状态类和上下文就能引入新状态。
- 通过消除臃肿的状态机条件语句简化上下文代码。
缺点:
- 如果状态机只有很少的几个状态,或者很少发生改变,那么应用该模式可能会显得小题大作。