设计模式(C++)-行为型模式-状态模式
一、状态模式概述
状态模式(State Pattern):是一种行为型设计模式,它允许一个对象在其内部状态改变时改变其行为,使得对象看起来似乎修改了其类。
核心思想:允许对象在内部状态发生改变时改变其行为,这个对象看起来就像是改变了它的类。
二、状态模式UML类图
状态模式场景
以抽奖活动为例。
有四个状态:
- 【抽奖状态】
- 【可以抽奖】
- 【发放奖品】
- 【奖品已领完】
抽奖活动的初始状态为【不能抽奖】状态,在扣除50积分后进入【可以抽奖】状态,【可以抽奖】状态有90%的几率不中奖,
不中奖就会回到【抽奖状态】,10%的几率中奖,中奖后进入【发放奖品】状态,当奖品数量为0是进入【奖品已领完】状态。
如果用switch/if实现起来比较复杂下面用状态模式实现。

三、代码实现
cpp
//state.h
#pragma once
/*
*状态模式
*每个人、事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)。
主要解决:
1.当状态数目不是很多的时候,Switch/Case 可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组
的Switch/Case 语句将是一件异常困难并且容易出错的事情。
2.状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性
和维护得不到保证。
举例:以抽奖活动为例。
有四个状态:
- 【抽奖状态】
- 【可以抽奖】
- 【发放奖品】
- 【奖品已领完】
抽奖活动的初始状态为【不能抽奖】状态,在扣除50积分后进入【可以抽奖】状态,【可以抽奖】状态有90%的几率不中奖,
不中奖就会回到【抽奖状态】,10%的几率中奖,中奖后进入【发放奖品】状态,当奖品数量为0是进入【奖品已领完】状态。
如果用switch/if实现起来比较复杂下面用状态模式实现
*/
#include <iostream>
#include <time.h>
class State {
public:
//扣除50积分
virtual void deductMeney() = 0;
//是否中奖
virtual bool raffle() = 0;
//发放奖品
virtual void dispensePrize() = 0;
};
void testState();
//state.cc
#include "state.h"
#include "raffleactivity.h"
/*
优点:
1.封装了转换规则。
2.枚举可能的状态,在枚举状态之前需要确定状态种类。
3.将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
4.允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
5.可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
*/
void testState() {
std::cout << "=================state start===============" << std::endl;
RaffleActivity* activity = new RaffleActivity(1);
for (int i = 0; i < 50; i++)
{
std::cout << "第" << i << "次抽奖" << std::endl;
activity->deductMoney();
activity->raffle();
std::cout << std::endl;
}
std::cout << "=================state end===============" << std::endl;
}
//norafflestate.h
#pragma once
#include <iostream>
#include "state.h"
class RaffleActivity;
//不能抽奖状态
class NoRaffleState :public State {
public:
NoRaffleState(RaffleActivity* activity);
//在不能抽奖状态是可以扣积分的,扣除积分后设置成可以抽奖状态
void deductMeney() override;
bool raffle() override;
void dispensePrize() override;
private:
RaffleActivity* m_activity;
};
//norafflestate.cc
#include "norafflestate.h"
#include "raffleactivity.h"
NoRaffleState::NoRaffleState(RaffleActivity* activity) {
m_activity = activity;
}
//在不能抽奖状态是可以扣积分的,扣除积分后设置成可以抽奖状态
void NoRaffleState::deductMeney(){
std::cout << "扣除50积分,可以抽奖了" << std::endl;
m_activity->setState(m_activity->getCanRaffleState());
}
bool NoRaffleState::raffle(){
std::cout << "扣了积分才能抽奖" << std::endl;
return false;
}
void NoRaffleState::dispensePrize(){
std::cout << "不能发放奖品" << std::endl;
}
//dispensestate.h
#pragma once
#include <iostream>
#include "state.h"
class RaffleActivity;
//发放奖品状态
class DispenseState :public State {
public:
DispenseState(RaffleActivity*activity);
void deductMeney() override;
bool raffle() override;
//发放奖品
void dispensePrize() override;
private:
RaffleActivity* m_activity;
};
//dispensestate.cc
#include "dispensestate.h"
#include "raffleactivity.h"
DispenseState::DispenseState(RaffleActivity*activity) {
m_activity = activity;
}
void DispenseState::deductMeney(){
std::cout << "不能扣除积分" << std::endl;
}
bool DispenseState::raffle(){
std::cout << "不能抽奖了" << std::endl;
return false;
}
//发放奖品
void DispenseState::dispensePrize(){
if (m_activity->getCount() > 0)
{
std::cout << "送出奖品" << std::endl;
m_activity->setState(m_activity->getNoRaffleState());
}
else
{
std::cout << "很遗憾,奖品发完了" << std::endl;
//奖品已经发完,后面的人就不能抽奖了
m_activity->setState(m_activity->getDispenseOutState());
}
}
//dispenseoutstate.h
#pragma once
#include <iostream>
#include "state.h"
class RaffleActivity;
//奖品发放完毕状态
class DispenseOutState :public State {
public:
DispenseOutState(RaffleActivity*activity);
void deductMeney() override;
bool raffle() override;
void dispensePrize() override;
private:
RaffleActivity* m_activity;
};
//dispenseoutstate.cc
#include "dispenseoutstate.h"
#include "raffleactivity.h"
DispenseOutState::DispenseOutState(RaffleActivity*activity) {
m_activity = activity;
}
void DispenseOutState::deductMeney(){
std::cout << "奖品已经发完了,请下次参加" << std::endl;
}
bool DispenseOutState::raffle(){
std::cout << "奖品已经发完了,请下次参加" << std::endl;
return false;
}
void DispenseOutState::dispensePrize(){
std::cout << "奖品已经发完了,请下次参加" << std::endl;
}
//canrafflestate.h
#pragma once
#include <iostream>
#include <time.h>
#include "state.h"
class RaffleActivity;
//能抽奖状态
class CanRaffleState :public State {
public:
CanRaffleState(RaffleActivity*activity);
//已经扣除积分了,不能再扣了
void deductMeney();
//可以抽奖,根据抽奖情况改成新的状态
bool raffle() override;
void dispensePrize() override;
private:
RaffleActivity* m_activity;
};
//canrafflestate.cc
#include "canrafflestate.h"
#include "raffleactivity.h"
CanRaffleState::CanRaffleState(RaffleActivity*activity) {
srand(time(NULL));
m_activity = activity;
}
//已经扣除积分了,不能再扣了
void CanRaffleState::deductMeney(){
std::cout << "已经扣过积分了" << std::endl;
}
//可以抽奖,根据抽奖情况改成新的状态
bool CanRaffleState::raffle(){
std::cout << "正在抽奖..." << std::endl;
int result = rand() % 10;
if (!result) {
//将activity的状态设置为发放奖品的状态
m_activity->setState(m_activity->getDispenseState());
return true;
}
else {
std::cout << "很遗憾没有抽中奖品" << std::endl;
//将activity的状态设置为不能抽奖的状态
m_activity->setState(m_activity->getNoRaffleState());
return false;
}
}
void CanRaffleState::dispensePrize(){
std::cout << "没中奖,不能发放奖品" << std::endl;
}
//raffleactivity.h
#pragma once
#include <iostream>
#include "state.h"
#include "canrafflestate.h"
#include "dispenseoutstate.h"
#include "dispensestate.h"
#include "norafflestate.h"
class RaffleActivity {
public:
RaffleActivity(int count) {
//初始化当前状态为 不能抽奖状态
this->state = getNoRaffleState();
//初始化奖品数量
this->count = count;
}
//扣分,调用当前状态的deductMoney
void deductMoney()
{
state->deductMeney();
}
//抽奖
void raffle()
{
//如果抽中奖了,则领奖品
if (state->raffle())
{
state->dispensePrize();
}
}
State* getState()const {
return state;
}
void setState(State*const state) {
this->state = state;
}
int getCount() {
return count--;
}
void setCount(const int count) {
this->count = count;
}
State* getNoRaffleState()const {
return noRaffleState;
}
void setNoRaffleState(State* const noRaffleState)
{
this->noRaffleState = noRaffleState;
}
State* getCanRaffleState() const
{
return canRaffleState;
}
void setCanRaffleState(State* const canRaffleState)
{
this->canRaffleState = canRaffleState;
}
State* getDispenseState() const
{
return dispenseState;
}
void setDispenseState(State* const dispenseState)
{
this->dispenseState = dispenseState;
}
State* getDispenseOutState() const
{
return dispenseOutState;
}
void setDispenseOutState(State* const dispenseOutState)
{
this->dispenseOutState = dispenseOutState;
}
private:
//state表示活动当前的状态
State* state = nullptr;
//奖品数量
int count = 0;
//四个属性 表示四种状态
State* noRaffleState = new NoRaffleState(this);
State* canRaffleState = new CanRaffleState(this);
State* dispenseState = new DispenseState(this);
State* dispenseOutState = new DispenseOutState(this);
};
四、状态模式优缺点
优点:
- 遵循单一职责原则
- 开闭原则的良好实践
- 消除庞大的条件分支语句
- 提高代码可维护性
- 状态转换更加安全可控
- 便于单元测试
缺点:
- 类数量爆炸
- 状态转移逻辑可能分散
- 上下文与状态间的循环依赖
- 内存和性能开销
- 状态共享困难
- 过度设计风险