状态是一种行为设计模式,让你能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。
状态模式的简单实现可以是基于条件语句的状态机,即switch case结构。但后期可能会造成臃肿。建议为对象的所有可能状态新建一个类,将所有状态的对应行为抽取到这些类中。
实例
Context.h
#ifndef CONTEXT_H_
#define CONTEXT_H_
#include <string>
#include <memory>
class AbstractState;
// 论坛账号
class ForumAccount {
public:
explicit ForumAccount(std::string name);
void set_state(std::shared_ptr<AbstractState> state) {
state_ = state;
}
std::shared_ptr<AbstractState> get_state() {
return state_;
}
std::string get_name() {
return name_;
}
void downloadFile(int score);
void writeNote(int score);
void replyNote(int score);
private:
std::shared_ptr<AbstractState> state_;
std::string name_;
};
#endif // CONTEXT_H_
Context.cpp
#include "Context.h"
#include "ConcreteState.h"
#include <string>
ForumAccount::ForumAccount(std::string name)
: name_(name), state_(std::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);
}
ForumAccount内包含一个状态AbstractState,ForumAccount执行具体命令时实际上是AbstractState执行具体命令,而AbstractState是基类,会根据积分在PrimaryState、MiddleState和HighState之间切换。因为AbstractState要切换状态,所以AbstractState要包含ForumAccount对象,切换状态时是ForumAccount对象set_state。
State.h
#ifndef STATE_H_
#define STATE_H_
#include <string>
#include <cstdio>
#include "Context.h"
class AbstractState {
public:
virtual void checkState() = 0;
void set_point(int point) {
point_ = point;
}
int get_point() {
return point_;
}
void set_state_name(std::string name) {
state_name_ = name;
}
std::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());
}
protected:
ForumAccount* account_;
int point_;
std::string state_name_;
};
#endif // STATE_H_
包含对象ForumAccount,积分变换可能会触发其set_state改变状态。
ConcreteState.h
#ifndef CONCRETE_STATE_H_
#define CONCRETE_STATE_H_
#include <cstdio>
#include "State.h"
// 具体状态类: 新手
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;
};
#endif // CONCRETE_STATE_H_
ConcreteState.cpp
#include "ConcreteState.h"
#include <memory>
void PrimaryState::checkState() {
if (point_ >= 1000) {
account_->set_state(std::make_shared<HighState>(this));
} else if (point_ >= 100) {
account_->set_state(std::make_shared<MiddleState>(this));
}
}
void MiddleState::checkState() {
if (point_ >= 1000) {
account_->set_state(std::make_shared<HighState>(this));
} else if (point_ < 100) {
account_->set_state(std::make_shared<PrimaryState>(this));
}
}
void HighState::checkState() {
if (point_ < 100) {
account_->set_state(std::make_shared<PrimaryState>(this));
} else if (point_ < 1000) {
account_->set_state(std::make_shared<MiddleState>(this));
}
}
main.cpp
#include "Context.h"
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;
}
编译运行:
$g++ -g main.cpp Context.cpp ConcreteState.cpp -o state -std=c++11
$./state
账号TOMOCAT注册成功!
TOMOCAT发布留言, 增加20积分。
TOMOCAT剩余积分为20, 当前级别为新手。
对不起, TOMOCAT没有下载文件的权限!
TOMOCAT回复留言, 增加100积分。
TOMOCAT剩余积分为120, 当前级别为高手。
TOMOCAT发布留言, 增加80积分。
TOMOCAT剩余积分为200, 当前级别为高手。
TOMOCAT下载文件, 扣除80积分。
TOMOCAT剩余积分为120, 当前级别为高手。
TOMOCAT发布留言, 增加2000积分。
TOMOCAT剩余积分为2120, 当前级别为专家。
TOMOCAT下载文件, 扣除40积分。
TOMOCAT剩余积分为2080, 当前级别为专家。