设计模式【15】——状态模式(State模式)

2023-11-14


前言

每个人、事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)。最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过。在出口处也是验票,如果正确你就可以 ok,否则就不让你通过。通常我们在实现这类系统会使用到很多的 Switch/Case 语句,Case 某种状态,发生什么动作,Case 另外一种状态,则发生另外一种状态。但是这种实现方式也存在部分缺点:
1)当状态数目不是很多的时候,Switch/Case 可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组的 Switch/Case 语句将是一件异常困难并且容易出错的事情。
2)状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。


一、状态模式(State模式)

State 模式就是被用来解决上述问题的,在 State 模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的 case 分支语句,并且这些分支依赖于对象的状态。State 模式将每一个分支都封装到独立的类中。UML图如下:
State模式示意图


二、具体源码

1.State.h

代码如下(示例):

#pragma once

#ifndef _STATE_H_ 
#define _STATE_H_ 

#include <iostream>


class Context;

class State
{
public:

  State();
  virtual ~State();
  virtual void OperationInterface(Context*) = 0;
  virtual void OperationChangeState(Context*) = 0;

protected:
  bool ChangeState(Context* con, State* st);
private:
  //bool ChangeState(Context* con,State* st); 
};

class ConcreteStateA :public State
{
public:

  ConcreteStateA();
  virtual ~ConcreteStateA();
  virtual void OperationInterface(Context*);
  virtual void OperationChangeState(Context*);

protected:
private:
};

class ConcreteStateB :public State
{
public:

  ConcreteStateB();
  virtual ~ConcreteStateB();
  virtual void OperationInterface(Context*);
  virtual void OperationChangeState(Context*);

protected:
private:
};
#endif //_STATE_H_

2.State.cpp

代码如下(示例):

#include "State.h" 
#include "Context.h"

State::State()
{
}

State::~State()
{
}

void State::OperationInterface(Context* con)
{
  std::cout << "State::.." << std::endl;
}

bool State::ChangeState(Context* con, State* st)
{
  con->ChangeState(st);
  return true;
}

void State::OperationChangeState(Context* con)
{

}

/// 
ConcreteStateA::ConcreteStateA()
{
}

ConcreteStateA::~ConcreteStateA()
{
}

void ConcreteStateA::OperationInterface(Context* con)
{
  std::cout << "ConcreteStateA::OperationInterface ......"<<std::endl; 
}

void ConcreteStateA::OperationChangeState(Context* con)
{
  OperationInterface(con);
  this->ChangeState(con, new ConcreteStateB());
}

/// 
ConcreteStateB::ConcreteStateB()
{
}

ConcreteStateB::~ConcreteStateB()
{
}

void ConcreteStateB::OperationInterface
(Context* con)
{
  std::cout << "ConcreteStateB::OperationInterface......" << std::endl;
}

void ConcreteStateB::OperationChangeState(Context* con)
{
  OperationInterface(con);
  this->ChangeState(con, new ConcreteStateA());
}

3.Context.h

代码如下(示例):

#pragma once

#ifndef _CONTEXT_H_ 
#define _CONTEXT_H_ 

#include<iostream>

class State;

class Context
{
public:

  Context();
  Context(State* state);
  ~Context();
  void OprationInterface();
  void OperationChangState();
  bool ChangeState(State* state);

protected:
private:
  State* _state;
};
#endif //_CONTEXT_H_

4.Context.cpp

代码如下(示例):

#include "Context.h" 
#include "State.h" 

Context::Context()
{
}

Context::Context(State* state)
{
  this->_state = state;
}

Context::~Context()
{
  delete _state;
}

void Context::OprationInterface()
{
  _state->OperationInterface(this);
}

bool Context::ChangeState(State* state)
{
  ///_state->ChangeState(this,state); 
  this->_state = state;
  return true;
}

void Context::OperationChangState()
{
  _state->OperationChangeState(this);
}

5.main.cpp

代码如下(示例):

#include "Context.h" 
#include "State.h" 

int main(int argc, char* argv[])
{
  State* st = new ConcreteStateA();
  Context* con = new Context(st);

  con->OperationChangState();
  con->OperationChangState();
  con->OperationChangState();

  if (con != NULL)
    delete con;

  if (st != NULL)
    st = NULL;

  return 0;
}

State 模式在实现中,有两个关键点:
1)将 State 声明为 Context 的友元类(friend class),其作用是让 State 模式访问 Context 的 protected 接口 ChangeSate。
2)State 及其子类中的操作都将 Context*传入作为参数,其主要目的是 State 类可以通过这个指针调用 Context 中的方法(在本示例代码中没有体现)。这也是 State 模式和 Strategy模式的最大区别所在。


三、运行结果

State模式运行结果如下:
 State模式运行结果


总结

从上述内容可以看出, State 模式和 Strategy 模式又很大程度上的相似:它们都有一个 Context 类,都是通过组合的方式给一个具有多个派生类的多态基类实现 Context 的算法逻辑。

两者最大的差别就是 State 模式中派生类持有指向 Context 对象的引用,并通过这个引用调用 Context 中的方法,但在 Strategy 模式中就没有这种情况。实际上 State 模式和 Strategy 模式的区别还在于它们所关注的点不
尽相同:State 模式主要是要适应对象对于状态改变时的不同处理策略的实现,而 Strategy则主要是具体算法和实现接口的解耦

State 模式很好地实现了对象的状态逻辑和动作实现的分离,状态逻辑分布在 State 的派生类中实现,而动作实现则可以放在 Context 类中实现(这也是为什么 State 派生类需要拥有一个指向 Context 的指针)。这使得两者的变化相互独立,改变 State 的状态逻辑可以很容易复用 Context 的动作,也可以在不影响 State 派生类的前提下创建 Context 的子类来更改或替换动作实现。

State 模式问题主要是逻辑分散化,状态逻辑分布到了很多的 State 的子类中,很难看到整个的状态逻辑图,降低了系统的可维护性


本文参考《设计模式精解-GoF 23 种设计模式解析附 C++实现源码》,对内容进行整理,方便大家学习。如想学习详细内容,请参考此书。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式【15】——状态模式(State模式) 的相关文章

  • Python开发环境Wing IDE如何使用调试功能

    在使用Wing IDE开始调试的时候 需要设置断点的行 读取GetItemCount函数的返回 这可以通过单击行并选择Break工具栏条目 或通过单击行左边的黑色边缘 断点应该以实心红圈的形式出现 接下来使用绿色箭头图标开始调试或在Debu

随机推荐

  • 基于微信小程序的健身小助手小程序

    文末联系获取源码 开发语言 Java 框架 ssm JDK版本 JDK1 8 服务器 tomcat7 数据库 mysql 5 7 8 0 数据库工具 Navicat11 开发软件 eclipse myeclipse idea Maven包
  • Mysql中分组后取最新的一条数据

    在 SQL 中 你可以使用子查询和 ORDER BY 子句来实现按照特定字段进行分组 并获取每个分组中最新的一条记录 SELECT t1 FROM your table t1 INNER JOIN SELECT id MAX timesta
  • 云技术平台赋能媒体融合发展创新

    欢迎大家前往腾讯云技术社区 获取更多腾讯海量技术实践干货哦 作者 熊普江 媒体行业是传统而又新兴的行业 在数字化 信息化 移动化快速演进的今天 无论是用户 社会还是行业 政府都意识到 传统媒体与新兴媒体融合发展是必然之路 但媒体融合需要内容
  • 2021-07-14 React 代码规范整理

    文章目录 React 代码规范 1 基础规则 2 组件声明 1 组件名称和定义该组件的文件名称建议要保持一致 2 不要使用 displayName 属性来定义组件的名称 应该在 class 或者 function 关键字后面直接声明组件的名
  • 烟花代码

    div div
  • 日志-Log4J

    日志 程序中的日志可以用来记录程序在运行的时候点点滴滴 并可以进行永久存储 日志和输出语句的区别 输出语句 日志技术 取消日志 需要修改代码 灵活性比较差 不需要修改代码 灵活性比较好 输出位置 只能是控制台 可以将日志信息写入到文件或者数
  • R语言之 as.formula()

    今天学到一个东西 R 语言回归函数里面的公式函数 as formula 其作用就是将字符串转换成公式 gt aa ReadCount Age BMI Sex HAMD 1 1 Sex gt aa 1 ReadCount Age BMI Se
  • MFC之滑块与旋转控件23

    1 滑块 首先看看滑块相关的内容 从sitch看到 滑块分为五个内容 分别是矩形滑块位置 滑块左右两边的箭头 和矩形滑块分别距离左右两边的空间 1 先添加滑块控件和显示矩形滑块的位置编辑区 2 右击编辑区添加变量为int类型 3 添加滑块为
  • Qt实现不同项目的信号传递

    Qt实现windows通信 不同项目的窗口通信 有关Qt通信的知识 windows要实现不同项目窗口通信 需要用类似于windows h的api接口 数据传输主要通过 typedef struct tagCOPYDATASTRUCT cds
  • nowcoder-15165-字符串问题-kmp

    题目描述 有一个字符串 让你找到这个字符串 S 里面的子串T 这个子串 T 必须满足即使这个串的前缀 也是这个 串的后缀 并且 在字符串中也出现过一次的 提示 要求满足前后缀的同时也要在字符串中出现一次 只是前后缀可不行 输出最长满足要求字
  • Shell脚本进阶版合集

    文章目录 一 Linux监控服务端口脚本 二 Linux编译安装Nginx脚本 三 Linux监控一个主机状态脚本 四 Linux统计内存 CPU使用前十进程脚本 五 Linux 磁盘I O列长度监控脚本 六 Linux计算内存使用率占比
  • 【多元统计分析】09.独立性检验与正态性检验

    文章目录 九 独立性检验和正态性检验 1 独立性检验 2 一元数据正态性检验 3 多元数据的正态性检验 回顾总结 九 独立性检验和正态性检验 1 独立性检验 独立性检验 指的是将一个多元总体 X N p
  • 【cmake】find_package设置查找路径

    1 find package的作用与实例 用来查找第三方依赖包的 cmake文件 并根据 cmake文件生成依赖包的头文件目录和库文件路径等 CMakeLists txt实例 find package Protobuf REQUIRED i
  • [大话设计模式C++版] 第14章 老板回来,我不知道 —— 观察者模式

    源码可以在这里找到 大话设计模式C 版 双向耦合的代码 Secretary h 秘书类 include
  • numpy对array索引

    numpy中array索引 对numpy的array索引时总是容易出错 借此机会总结一下numpy中array最常用的索引方法 1 单个元素的索引 In 1 1 1 一维array 单个元素的索引使用整数 import numpy as n
  • 能把百度玩出花样的人肯定不简单,分享几个鲜为人知的搜索引擎高级语法

    作者主页 士别三日wyx 作者简介 CSDN top100 阿里云博客专家 华为云享专家 网络安全领域优质创作者 专栏简介 此文章已录入专栏 网络安全快速入门 渗透过程中 通常会使用搜索引擎来收集重要信息 确定攻击点 Google 黑客语法
  • 牛客题集——Cook Steak(警示 理解题意)

    Cook Steak 题目链接 题目译文 对于烤牛排 ZJH认为它需要N道烧烤工序 每道工序都在一个温度范围内 li ri 就一分钟而言 只有在这种情况下 烤牛排才是最好的 幸运的是 厨房里的设备已经配备了人工智能 可以在最短的时间内快速完
  • 浏览器缓存的位置

    缓存位置的类型 缓存位置有四种 各自有优先级 当依次查找缓存且都没有命中的时候 才会去请求网络 Service Worker Memory Cache Disk Cache Push Cache Service Worker Service
  • Web基础(从零开始)——HTML下拉菜单 select标签详解

  • 设计模式【15】——状态模式(State模式)

    文章目录 前言 一 状态模式 State模式 二 具体源码 1 State h 2 State cpp 3 Context h 4 Context cpp 5 main cpp 三 运行结果 总结 前言 每个人 事物在不同的状态下会有不同表