介绍
状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象,状态模式是一种对象行为模式。
实现
myclass.h
//
// Created by yuwp on 2024/1/12.
//
#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H
#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>
class State { // 抽象状态类
public:
virtual void handle() = 0;
};
class Context { // 环境类
public:
Context();
void setState(const std::shared_ptr<State> &state);
void request(int num);
void changeState();
private:
std::shared_ptr<State> m_state;
int m_value;
};
class ConcreteStateA : public State { // 具体状态类A
public:
void handle() override;
};
class ConcreteStateB : public State { // 具体状态类B
public:
void handle() override;
};
#endif //DESIGNPATTERNS_MYCLASS_H
myclass.cpp
//
// Created by yuwp on 2024/1/12.
//
#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>
Context::Context() {
m_value = 0;
m_state.reset(new ConcreteStateA());
}
void Context::setState(const std::shared_ptr<State> &state) {
m_state = state;
}
void Context::request(int num) {
int tmp = m_value;
m_value += num;
if ((tmp >= 0 && m_value < 0) || (tmp < 0 && m_value >= 0)) {
changeState();
}
m_state->handle();
}
void Context::changeState() {
std::cout << "切换状态" << std::endl;
if (m_value >= 0) {
m_state.reset(new ConcreteStateA());
} else {
m_state.reset(new ConcreteStateB());
}
}
void ConcreteStateA::handle() {
std::cout << "ConcreteStateA::handle()" << std::endl;
}
void ConcreteStateB::handle() {
std::cout << "ConcreteStateB::handle()" << std::endl;
}
main.cpp
#include <iostream>
#include <mutex>
#include "myclass.h"
int main() {
Context *context = new Context;
context->request(0);
std::cout << "-------------------" << std::endl;
context->request(-10);
std::cout << "-------------------" << std::endl;
context->request(20);
return 0;
}
总结
优点
1. 封装了状态的转换规则。在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
2. 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
3. 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块。状态模式可以避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
4. 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点
1. 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
2. 状态模式的程序结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
3. 状态模式对开闭原则的支持并不太好。增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
适用场景
1. 对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化。
2. 在代码中包含大量与对象状态有关的条件语句。这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。
练习
myclass.h
//
// Created by yuwp on 2024/1/12.
//
#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H
#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>
class Level { // 抽象状态类
public:
virtual void play() = 0;
virtual void doubleScore();
virtual void changeCards();
virtual void peekCards();
};
class CardsGame {
public:
CardsGame();
~CardsGame();
void play(int score); // 模拟玩游戏得分
void doubleScore();
void changeCards();
void peekCards();
private:
Level *m_level; // 当前等级
Level *m_primary; // 入门级
Level *m_secondary; // 熟练级
Level *m_professional; // 高手级
Level *m_final; // 骨灰级
int m_score; // 得分
};
class PrimaryLevel : public Level {
public:
void play() override;
void doubleScore() override;
void changeCards() override;
void peekCards() override;
};
class SecondaryLevel : public Level {
public:
void play() override;
void changeCards() override;
void peekCards() override;
};
class ProfessionalLevel : public Level {
public:
void play() override;
void peekCards() override;
};
class FinalLevel : public Level {
public:
void play() override;
};
#endif //DESIGNPATTERNS_MYCLASS_H
myclass.cpp
//
// Created by yuwp on 2024/1/12.
//
#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>
void Level::doubleScore() {
std::cout << "胜利积分加倍" << std::endl;
}
void Level::changeCards() {
std::cout << "换牌" << std::endl;
}
void Level::peekCards() {
std::cout << "偷看他人的牌" << std::endl;
}
CardsGame::CardsGame() {
m_primary = new PrimaryLevel();
m_secondary = new SecondaryLevel();
m_professional = new ProfessionalLevel();
m_final = new FinalLevel();
m_level = m_primary;
m_score = 0;
}
CardsGame::~CardsGame() {
if (m_primary) {
delete m_primary;
}
if (m_secondary) {
delete m_secondary;
}
if (m_professional) {
delete m_professional;
}
if (m_final) {
delete m_final;
}
}
void CardsGame::play(int score) {
m_level->play();
m_score += score;
if (m_score < 100) {
if (m_level != m_primary) {
std::cout << "切换到入门级" << std::endl;
m_level = m_primary;
}
} else if (m_score < 500) {
if (m_level != m_secondary) {
std::cout << "切换到熟练级" << std::endl;
m_level = m_secondary;
}
} else if (m_score < 1000) {
if (m_level != m_professional) {
std::cout << "切换到高手级" << std::endl;
m_level = m_professional;
}
} else {
if (m_level != m_final) {
std::cout << "切换到骨灰级" << std::endl;
m_level = m_final;
}
}
}
void CardsGame::changeCards() {
m_level->changeCards();
}
void CardsGame::doubleScore() {
m_level->doubleScore();
}
void CardsGame::peekCards() {
m_level->peekCards();
}
void PrimaryLevel::play() {
std::cout << "入门级游戏开始" << std::endl;
}
void PrimaryLevel::doubleScore() {
std::cout << "入门级不支持游戏胜利积分加倍功能" << std::endl;
}
void PrimaryLevel::changeCards() {
std::cout << "入门级不支持换牌功能" << std::endl;
}
void PrimaryLevel::peekCards() {
std::cout << "入门级不支持偷看他人的牌功能" << std::endl;
}
void SecondaryLevel::play() {
std::cout << "熟练级游戏开始" << std::endl;
}
void SecondaryLevel::changeCards() {
std::cout << "熟练级不支持换牌功能" << std::endl;
}
void SecondaryLevel::peekCards() {
std::cout << "熟练级不支持偷看他人的牌功能" << std::endl;
}
void ProfessionalLevel::play() {
std::cout << "高手级游戏开始" << std::endl;
}
void ProfessionalLevel::peekCards() {
std::cout << "高手级不支持偷看他人的牌功能" << std::endl;
}
void FinalLevel::play() {
std::cout << "骨灰级游戏开始" << std::endl;
}
main.cpp
#include <iostream>
#include <mutex>
#include "myclass.h"
int main() {
CardsGame *cardsGame = new CardsGame();
cardsGame->doubleScore();
cardsGame->changeCards();
cardsGame->peekCards();
cardsGame->play(200);
std::cout << "------------------------" << std::endl;
cardsGame->doubleScore();
cardsGame->changeCards();
cardsGame->peekCards();
cardsGame->play(500);
std::cout << "------------------------" << std::endl;
cardsGame->doubleScore();
cardsGame->changeCards();
cardsGame->peekCards();
cardsGame->play(500);
std::cout << "------------------------" << std::endl;
cardsGame->doubleScore();
cardsGame->changeCards();
cardsGame->peekCards();
cardsGame->play(0);
delete cardsGame;
return 0;
}