设计模式-行为型模式之状态模式

news2025/1/21 1:00:14

4. 状态模式

4.1. 模式动机

  • 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。

  • 在UML中可以使用状态图来描述对象状态的变化。

4.2. 模式定义

状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式

4.3. 模式结构

状态模式包含如下角色:

  • Context: 环境类

  • State: 抽象状态类

  • ConcreteState: 具体状态类

4.4. 时序图

4.5. 代码分析

#include <iostream>
#include "Context.h"
#include "ConcreteStateA.h"
#include "ConcreteStateB.h"

using namespace std;

int main(int argc, char *argv[])
{
    char a = '0';
    if('0' == a)
        cout << "yes" << endl;
    else
        cout << "no" << endl;
    
    Context * c = new Context();
    c->request();
    c->request();
    c->request();

    delete c;
    return 0;
}
///
//  Context.h
//  Implementation of the Class Context
//  Created on:      09-十月-2014 17:20:59
//  Original author: colin
///

#if !defined(EA_F245CF81_2A68_4461_B039_2B901BD5A126__INCLUDED_)
#define EA_F245CF81_2A68_4461_B039_2B901BD5A126__INCLUDED_

#include "State.h"

class Context
{

public:
    Context();
    virtual ~Context();

    void changeState(State * st);
    void request();

private:
    State *m_pState;
};
#endif // !defined(EA_F245CF81_2A68_4461_B039_2B901BD5A126__INCLUDED_)
///
//  Context.cpp
//  Implementation of the Class Context
//  Created on:      09-十月-2014 17:20:59
//  Original author: colin
///

#include "Context.h"
#include "ConcreteStateA.h"

Context::Context(){
    //default is a
    m_pState = ConcreteStateA::Instance();
}

Context::~Context(){
}

void Context::changeState(State * st){
    m_pState = st;
}

void Context::request(){
    m_pState->handle(this);
}
///
//  ConcreteStateA.h
//  Implementation of the Class ConcreteStateA
//  Created on:      09-十月-2014 17:20:58
//  Original author: colin
///

#if !defined(EA_84158F08_E96A_4bdb_89A1_4BE2E633C3EE__INCLUDED_)
#define EA_84158F08_E96A_4bdb_89A1_4BE2E633C3EE__INCLUDED_

#include "State.h"

class ConcreteStateA : public State
{

public:
    virtual ~ConcreteStateA();
    
    static State * Instance();
    
    virtual void handle(Context * c);

private:
    ConcreteStateA();
    static State * m_pState;
};
#endif // !defined(EA_84158F08_E96A_4bdb_89A1_4BE2E633C3EE__INCLUDED_)
///
//  ConcreteStateA.cpp
//  Implementation of the Class ConcreteStateA
//  Created on:      09-十月-2014 17:20:58
//  Original author: colin
///

#include "ConcreteStateA.h"
#include "ConcreteStateB.h"
#include "Context.h"
#include <iostream>
using namespace std;

State * ConcreteStateA::m_pState = NULL;
ConcreteStateA::ConcreteStateA(){
}

ConcreteStateA::~ConcreteStateA(){
}

State * ConcreteStateA::Instance()
{
    if ( NULL == m_pState)
    {
        m_pState = new ConcreteStateA();
    }
    return m_pState;
}

void ConcreteStateA::handle(Context * c){
    cout << "doing something in State A.\n done,change state to B" << endl;
    c->changeState(ConcreteStateB::Instance());
}

运行结果:

4.6. 模式分析

  • 状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为。

  • 状态模式的关键是引入了一个抽象类来专门表示对象的状态,这个类我们叫做抽象状态类,而对象的每一种具体状态类都继承了该类,并在不同具体状态类中实现了不同状态的行为,包括各种状态之间的转换。

在状态模式结构中需要理解环境类与抽象状态类的作用:

  • 环境类实际上就是拥有状态的对象,环境类有时候可以充当状态管理器(State Manager)的角色,可以在环境类中对状态进行切换操作。

  • 抽象状态类可以是抽象类,也可以是接口,不同状态类就是继承这个父类的不同子类,状态类的产生是由于环境类存在多个状态,同时还满足两个条件: 这些状态经常需要切换,在不同的状态下对象的行为不同。因此可以将不同对象下的行为单独提取出来封装在具体的状态类中,使得环境类对象在其内部状态改变时可以改变它的行为,对象看起来似乎修改了它的类,而实际上是由于切换到不同的具体状态类实现的。由于环境类可以设置为任一具体状态类,因此它针对抽象状态类进行编程,在程序运行时可以将任一具体状态类的对象设置到环境类中,从而使得环境类可以改变内部状态,并且改变行为。

4.7. 实例

TCPConnection

这个示例来自《设计模式》,展示了一个简化版的TCP协议实现; TCP连接的状态有多种可能,状态之间的转换有相应的逻辑前提; 这是使用状态模式的场合;

结构图:

时序图:

4.8. 优点

状态模式的优点

  • 封装了转换规则

  • 枚举可能的状态,在枚举状态之前需要确定状态种类。

  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。

  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。

  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

4.9. 缺点

状态模式的缺点

  • 状态模式的使用必然会增加系统类和对象的个数

  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。

  • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

4.10. 适用环境

在以下情况下可以使用状态模式:

  • 对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。

  • 代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。

4.11. 模式应用

状态模式在工作流或游戏等类型的软件中得以广泛使用,甚至可以用于这些系统的核心功能设计,如在政府OA办公系统中,一个批文的状态有多种:尚未办理;正在办理;正在批示;正在审核;已经完成等各种状态,而且批文状态不同时对批文的操作也有所差异。使用状态模式可以描述工作流对象(如批文)的状态转换以及不同状态下它所具有的行为。

4.12. 模式扩展

共享状态

在有些情况下多个环境对象需要共享同一个状态,如果希望在系统中实现多个环境对象实例共享一个或多个状态对象,那么需要将这些状态对象定义为环境的静态成员对象。

简单状态模式与可切换状态的状态模式

简单状态模式

简单状态模式是指状态都相互独立,状态之间无须进行转换的状态模式,这是最简单的一种状态模式。对于这种状态模式,每个状态类都封装与状态相关的操作,而无须关心状态的切换,可以在客户端直接实例化状态类,然后将状态对象设置到环境类中。如果是这种简单的状态模式,它遵循“开闭原则”,在客户端可以针对抽象状态类进行编程,而将具体状态类写到配置文件中,同时增加新的状态类对原有系统也不造成任何影响。

可切换状态的状态模式

多数的状态模式都是可以切换状态的状态模式,在实现状态切换时,在具体状态类内部需要调用环境类Context的setState()方法进行状态的转换操作,在具体状态类中可以调用到环境类的方法,因此状态类与环境类之间通常还存在关联关系或者依赖关系。通过在状态类中引用环境类的对象来回调环境类的setState()方法实现状态的切换。在这种可以切换状态的状态模式中,增加新的状态类可能需要修改其他某些状态类甚至环境类的源代码,否则系统无法切换到新增状态。

4.13. 总结

  • 状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象,状态模式是一种对象行为型模式

  • 状态模式包含三个角色环境类又称为上下文类,它是拥有状态的对象,在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象,可以定义初始状态;抽象状态类用于定义一个接口以封装与环境类的一个特定状态相关的行为;具体状态类是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

  • 状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为。

  • 状态模式的主要优点在于封装了转换规则并枚举可能的状态,它将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为,还可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数;其缺点在于使用状态模式会增加系统类和对象的个数,且状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,对于可以切换状态的状态模式不满足“开闭原则”的要求

  • 状态模式适用情况包括:对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为;代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。

[上一节]设计模式-行为型模式之观察者模式

[下一节]设计模式-行为型模式之策略模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GPT大模型之后,谷歌、微软、百度们AI争霸的下一战

又来了一个大模型&#xff01; 4 月 14 日&#xff0c;雷军宣布推出小米的大规模语言模型&#xff1b;4 月 11 日&#xff0c;在阿里云峰会上&#xff0c;阿里云智能首席技术官周靖人正式宣布推出大规模语言模型——通义千问&#xff0c;并宣布要在钉钉、天猫精灵等阿里所有产品…

Ghidra使用之Options页面功能介绍

Apply Processor Defined Lables 在Ghidra中,apply processor defined labels功能可以为汇编代码中的地址和数据自动添加注释,这可以大大提高反汇编代码的可读性。 使用步骤如下: 打开您要反汇编的文件,进入Code Browser窗口。点击Edit菜单,选择Apply Processor Defined Labels…

Mysql不同服务器跨库查询解决方案

项目场景&#xff1a; Mysql在不同服务器实现跨库查询&#xff0c;类似dblink。 解决方案&#xff1a; 在两台不同服务器&#xff0c;实现跨库查询&#xff0c;其实现原理类似一个虚拟映射,需要用到mysql的另一个存储引擎Federated&#xff0c;FEDERATED存储引擎访问在远程数据…

另一个世界的人

休假 赶在假期无效前&#xff0c;休了个长假回了趟老家。不仅是为了逃离工作的繁忙&#xff0c;也为了看望老家的婆婆爷爷。回趟老家对于我们这种社会人来说确实太难了&#xff0c;如果只是周末回去也就休息一天就得往回赶&#xff0c;太累。自己的假期本来就不多&#xff0c;如…

如何对HDFS进行节点内(磁盘间)数据平衡

当HDFS的DataNode节点挂载多个磁盘时&#xff0c;往往会出现两种数据不均衡的情况&#xff1a; 1.不同DataNode节点间数据不均衡&#xff1b; 2.挂载数据盘的磁盘间数据不均衡。 特别是这种情况&#xff1a;当DataNode原来是挂载了几个数据盘&#xff0c;当磁盘占用率很高之…

7.1 参数的点估计

小结&#xff1a; 点估计是一种统计推断方法&#xff0c;它用于通过样本数据估计总体参数的值。在统计学中&#xff0c;总体是指一个包含所有个体的集合&#xff0c;而样本是从总体中选出的一部分个体。总体参数是总体的某种特征&#xff0c;如平均值、标准差、比例等。 点估…

全志V851s、V853内g2d模块sample深究

1. g2d 模块概述 g2d 主要功能&#xff1a; 1&#xff09;旋转&#xff1a;支持90、180、270旋转&#xff1b; 2&#xff09;镜像反转&#xff1a;H / V; 3) scale&#xff1a;放缩 4&#xff09;格式转换&#xff1a;yuv 转 rgb 等&#xff0c;多种格式相互间转换&#xff1b…

python学习——缺失值、重复值处理、排序及替换

文章目录 1 缺失值处理1.1 查看缺失值 df.isnull()1.2 统计缺失值 df.isnull().sum()1.3 删除缺失值 df.drop()1.4 填充缺失值 df.fillna()1.4.1 固定值填充 df.fillna(value)1.4.2 线性插值填充 df.fillna(df.interpolate()) 2 重复值处理2.1 查看重复值 df.duplicated()2.2 筛…

最佳实践:Android应用中的网络请求和数据缓存

最佳实践&#xff1a;Android应用中的网络请求和数据缓存 网络请求在Android应用中的重要性 在现代移动应用中&#xff0c;网络请求扮演着重要的角色&#xff0c;涉及到数据的获取、上传、更新等功能。网络请求在Android应用中具有关键地位&#xff0c;对于提供优秀的用户体验和…

如何使用山海鲸可视化绘制堆叠双轴图?

简介 堆叠双轴图是一种数据可视化图表类型&#xff0c;它结合了堆叠柱状图和双轴图的特点&#xff0c;将两个或更多个数据系列以不同的颜色或图案堆叠在一起&#xff0c;同时使用两个垂直轴来表示不同的数据范围&#xff0c;以显示它们之间的关系和趋势。其中一个轴通常代表数…

一个实例讲解如何使用BP神经网络(附代码)

本站原创文章&#xff0c;转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com BP神经网络是一个广泛应用的模型&#xff0c; 本文以一个实例作为主线&#xff0c;面向初学者讲解如何构建和使用一个BP神经网络 帮助没接触进BP神经网络的初学者&#xff0c;快速上手BP神经网络…

传统机器学习(四)聚类算法DBSCAN

传统机器学习(四)聚类算法DBSCAN 1.1 算法概述 DBSCAN&#xff08;Density-Based Spatial Clustering of Applications with Noise&#xff0c;具有噪声的基于密度的聚类方法&#xff09;是一种基于密度的空间聚类算法。 该算法将具有足够密度的区域划分为簇&#xff0c;并在…

【C++学习】类和对象--对象特性(2)

静态成员 静态成员就是在成员变量和成员函数前加上关键字static&#xff0c;称为静态成员。 静态成员变量&#xff1a; 所有对象共享同一份数据 在编译阶段分配内存 类内声明&#xff0c;类外初始化 静态成员函数&#xff1a; 所有对象共享同一个函数 静态成员函数只能访问静态…

MyBatis小技巧

一、MyBatis中接口代理机制及使用 我们不难发现&#xff0c;以前编写dao/mapper实现类中的方法代码很固定&#xff0c;基本上就是一行代码&#xff0c;通过SqlSession对象调用insert、delete、update、select等方法&#xff0c;这个类中的方法没有任何业务逻辑&#xff0c;既然…

【Cadence】 ADS Dynamic Link使用教程

ADS Dynamic Link使用教程 1.新建一个Cadence Schematic2.打开ADS3.在ADS新建Schematic4.加入控件OPTIONS5.加入Netlist6.仿真测试7.写在最后 1.新建一个Cadence Schematic 随便搭了一个Cascode 注意这个时候不要新建symbol 2.打开ADS 3.在ADS新建Schematic 选择对应的Cade…

BI技巧丨计算组环形图

当我们使用Power BI时&#xff0c;会发现其内置了许多可视化组件&#xff0c;这些组件可以帮我们快速地创建各种类型的报表和仪表板&#xff0c;这是非常方便的。但是&#xff0c;我们也会发现&#xff0c;有些组件的细节功能并不是很完善&#xff0c;这就导致了我们在平常使用…

解决 mapper.xml 文件的 resultType 爆红问题:Cannot resolve symbol ‘xxx‘

解决mapper.xml文件的resultType爆红问题&#xff1a;Cannot resolve symbol xxx 1.问题描述2.问题分析3.问题解决3.1 配置注解&#xff08;推荐&#xff09;3.2 配置全类名3.3 删除插件 4.事件感悟 系统&#xff1a;Win10 JDK&#xff1a;1.8.0_333 IDEA&#xff1a;2022.2.4 …

【C++】Qt环境下的五子棋小程序

实现要求 这个程序是一个基于Qt的五子棋游戏&#xff0c;实现了人机对战和双人对战两种游戏模式&#xff0c;提供了基本的棋谱记录和游戏音效功能。程序采用了MVC模式&#xff0c;将数据与界面分离&#xff0c;实现了代码的模块化和可扩展性。主要构建包括三个文件&#xff0c…

Java - 数据结构,哈希表

一、概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在查找一个元素时&#xff0c;必须要经过关键码的多次比较。顺序查找时间复杂度为O(N)&#xff0c;平衡树中为树的高度&#xff0c;即O( )&#xff0c;搜索的效率取决于搜…

Ambari【部署 01】最新版本ambari-2.7.7下载编译打包安装使用(踩坑实录)

Ambari 官网 本次安装的为当前最新版本 2.7.7 文档 。 1.环境及准备 1.1 环境 # 系统 [roottcloud ~]# cat /etc/centos-release CentOS Linux release 7.9.2009 (Core) # JDK [roottcloud ~]# java -version java version "1.8.0_241" Java(TM) SE Runtime Envir…