一 定义

状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把不同状态的操作分散到不同的状态对象里去完成。在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事件产生互动时,其内部状态就会改变,从而使得系统的行为也随之发生变化。

二 ULM图

Context(环境类)又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象,定义客户端需要的接口,并且负责具体状态的切换。环境角色具有状态抽象角色定义的所有行为。
State(抽象状态类)它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中,负责对象状态定义,并且封装环境角色以实现状态切换。

ConcreteState(具体状态类)它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,简而言之,就是本状态下要做的事情,以及本状态如何过渡到其他状态。

三 状态模式的优点和缺点及可用场景

状态模式的优点

封装了转换规则,将所有与某个状态有关的行为放到一个状态对象中且提供了一个更好的方法来组织管理与特定状态相关的代码,将繁琐的状态条件判断语句转换为结构明朗的状态环境类,保证了可读性和可扩展性。
可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

状态模式的缺点

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

使用场景

状态模式的意图就是:允许一个对象在其内部状态改变时改变它的行为。简单来说当状态对象本身存在很多决定自身状态的条件分支时,而自身的行为又受条件分支的影响
1) •一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。
2) •代码中包含大量与对象状态有关的条件语句:一个操作中含有庞大的多分支的条件(if else(或switch case)语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

四 实例

1. 电梯

a. 电梯状态的切换示意图

b. ULM图

#include <iostream>class Context;//state
class LiftState
{
protected:Context* context;
public:virtual ~LiftState() = default;void setContext(Context* context){this->context = context;}//开电梯门virtual void open() = 0;//关电梯门virtual void close() = 0;//电梯运行virtual void run() = 0;//电梯停止virtual void stop() = 0;
};//context
class Context
{
public:static LiftState* openningState;static LiftState* closingState;static LiftState* runningState;static LiftState* stoppingState;LiftState& getLiftState(){return *liftState;}void setLiftState(LiftState* liftState){this->liftState = liftState;//把当前的环境通知到各个实现类中this->liftState->setContext(this);}void open(){liftState->open();}void close(){liftState->close();}void run(){liftState->run();}void stop(){liftState->stop();}private:LiftState* liftState;
};//ConcreteState
class OpenningState : public LiftState
{
public://打开电梯门void open(){std::cout << "门开启电梯..." << std::endl;}//开门状态下,可以按下“关门”按钮void close(){//将当前状态修改为关门状态context->setLiftState(Context::closingState);//关门context->getLiftState().close();}//开门状态下不能运行!void run(){//do nothing;}//开门状态下,按停止没效果void stop(){//do nothing}
};//关门状态
class ClosingState : public LiftState
{
public://打开电梯门void open(){//将当前状态修改为开门状态context->setLiftState(Context::openningState);context->getLiftState().open();}//关门状态void close(){std::cout << "电梯门关闭..." << std::endl;}//关门状态,可以按“运行”按钮void run(){//将当前状态修改为运行状态context->setLiftState(Context::runningState);context->getLiftState().run();}//关门状态下,可以切换到停止状态void stop(){//将当前状态修改为停止状态context->setLiftState(Context::stoppingState);context->getLiftState().stop();}
};//运行状态
class RunningState : public LiftState
{
public://运行状态,按“开门”按钮没效课void open(){//do nothing}//运行状态按“关门”按钮没效果void close(){//do nothing;}//运行状态void run(){std::cout << "电梯上下运行..." << std::endl;}//运行状态下,可按“停止”void stop(){//将当前状态修改为停止状态context->setLiftState(Context::stoppingState);context->getLiftState().stop();}
};//停止状态
class StoppingState : public LiftState
{
public://停止状态,可以打开电梯门void open(){//将当前状态修改为开门状态context->setLiftState(Context::openningState);context->getLiftState().open();}//停止状态下,可以按下“关门”按钮没效果void close(){//do nothing}//关门状态,可以按“运行”按钮!void run(){//将当前状态修改为运行状态context->setLiftState(Context::runningState);context->getLiftState().run();}//开门状态下,按停止没效果void stop(){std::cout << "电梯停止了..." << std::endl;}
};LiftState* Context::openningState = new OpenningState();
LiftState* Context::closingState  = new ClosingState();
LiftState* Context::runningState  = new RunningState();
LiftState* Context::stoppingState = new StoppingState();int main()
{Context context;context.setLiftState(Context::closingState);context.open();context.close();context.run();context.stop();delete Context::openningState;delete Context::closingState;delete Context::runningState;delete Context::stoppingState;return 0;
}

2. 一个工人一天的时间分为上班,睡觉,干其他事情三个时间段。我们可以用状态模式来表示一天中在干什么事情。

#include <iostream>
#include <memory>
#include <utility>class Worker;class State
{
public:virtual void doing(Worker* w) = 0;virtual ~State() = default;
};class WorkingState : public State
{
public:void doing(Worker* worker);
};class SleepingState : public State
{
public:void doing(Worker* worker);
};class OtherState : public State
{
public:void doing(Worker* worker);
};class Worker
{
public:Worker() :state{std::make_unique<WorkingState>()}{}void setState(std::unique_ptr<State> state){this->state = std::move(state);}double getHour(){return hour;}void setHour(double hou){hour = hou;}void requestDoing(){state->doing(this);}
private:std::unique_ptr<State> state;double hour;
};
//各个状态doing方法的实现
void WorkingState::doing(Worker* worker)
{if (worker->getHour() > 8 && worker->getHour() < 16){std::cout << "WorkingState!" << std::endl;}else{worker->setState(std::make_unique<OtherState>());worker->requestDoing();}
}void SleepingState::doing(Worker* worker)
{if (worker->getHour() >= 21 || worker->getHour() < 5){std::cout << "SleepingState!" << std::endl;}else{worker->setState(std::make_unique<OtherState>());worker->requestDoing();}
}void OtherState::doing(Worker* worker)
{if ((worker->getHour() >= 5 && worker->getHour() < 8)|| (worker->getHour() >= 16 && worker->getHour() < 21)){std::cout << "OtherState!" << std::endl;}else if (worker->getHour() >= 21 || worker->getHour() < 5){worker->setState(std::make_unique<SleepingState>());worker->requestDoing();}else{worker->setState(std::make_unique<WorkingState>());worker->requestDoing();}
}
//客户代码
int main()
{auto worker = std::make_unique<Worker>();worker->setHour(24);std::cout << "time 24" << std::endl;worker->requestDoing();worker->setHour(11.5);std::cout << "time 11.5" << std::endl;worker->requestDoing();worker->setHour(19);std::cout << "time 19" << std::endl;worker->requestDoing();return 0;
}

运行结果如下:

五 与其他相关模式

1)策略模式

状态模式和策略模式的实现方法非常类似,都是利用多态把一些操作分配到一组相关的简单的类中,因此很多人认为这两种模式实际上是相同的。
然而在现实世界中,策略(如促销一种商品的策略)和状态(如同一个按钮来控制一个电梯的状态,又如手机界面中一个按钮来控制手机)是两种完全不同的思想。当我们对状态和策略进行建模时,这种差异会导致完全不同的问题。例如,对状态进行建模时,状态迁移是一个核心内容;然而,在选择策略时,迁移与此毫无关系。另外,策略模式允许一个客户选择或提供一种策略,而这种思想在状态模式中完全没有。
       一个策略是一个计划或方案,通过执行这个计划或方案,我们可以在给定的输入条件下达到一个特定的目标。策略是一组方案,他们可以相互替换;选择一个策略,获得策略的输出。策略模式用于随不同外部环境采取不同行为的场合。我们可以参考微软企业库底层Object Builder的创建对象的strategy实现方式。而状态模式不同,对一个状态特别重要的对象,通过状态机来建模一个对象的状态;状态模式处理的核心问题是状态的迁移,因为在对象存在很多状态情况下,对各个business flow,各个状态之间跳转和迁移过程都是及其复杂的。
       例如一个工作流,审批一个文件,存在新建、提交、已修改、HR部门审批中、老板审批中、HR审批失败、老板审批失败等状态,涉及多个角色交互,涉及很多事件,这种情况下用状态模式(状态机)来建模更加合适;把各个状态和相应的实现步骤封装成一组简单的继承自一个接口或抽象类的类,通过另外的一个Context来操作他们之间的自动状态变换,通过event来自动实现各个状态之间的跳转。在整个生命周期中存在一个状态的迁移曲线,这个迁移曲线对客户是透明的。我们可以参考微软最新的WWF 状态机工作流实现思想。
      在状态模式中,状态的变迁是由对象的内部条件决定,外界只需关心其接口,不必关心其状态对象的创建和转化;
而策略模式里,采取何种策略由外部条件(C)决定。
      他们应用场景(目的)却不一样,State模式重在强调对象内部状态的变化改变对象的行为,Strategy模式重在外部对策略的选择,策略的选择由外部条件决定,
也就是说算法的动态的切换。但由于它们的结构是如此的相似,我们可以认为“状态模式是完全封装且自修改的策略模式”。即状态模式是封装对象内部的状态的,而策略模式是封装算法族的。

2)职责链模式

职责链模式和状态模式都可以解决If分支语句过多,
从定义来看,状态模式是一个对象的内在状态发生改变(一个对象,相对比较稳定,处理完一个对象下一个对象的处理一般都已确定),
而职责链模式是多个对象之间的改变(多个对象之间的话,就会出现某个对象不存在的现在,就像我们举例的公司请假流程,经理可能不在公司情况),这也说明他们两个模式处理的情况不同。
这两个设计模式最大的区别就是状态模式是让各个状态对象自己知道其下一个处理的对象是谁。
而职责链模式中的各个对象并不指定其下一个处理的对象到底是谁,只有在客户端才设定。
用我们通俗的编程语言来说,就是
状态模式:
  相当于If else if else;
  设计路线:各个State类的内部实现(相当于If,else If内的条件)
  执行时通过State调用Context方法来执行。
职责链模式:
  相当于Swich case
  设计路线:客户设定,每个子类(case)的参数是下一个子类(case)。
  使用时,向链的第一个子类的执行方法传递参数就可以。
就像对设计模式的总结,有的人采用的是状态模式,从头到尾,提前一定定义好下一个处理的对象是谁,而我采用的是职责链模式,随时都有可能调整链的顺序。

C++设计模式之状态模式(state)(行为型)相关推荐

  1. 【转】设计模式 ( 十七) 状态模式State(对象行为型)

    设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...

  2. 设计模式之状态模式(State)

    什么是状态? 我们在购物网站进行购物时,订单会产生几种状况:已下单.已付款.送货中.确定收货等状态. 所以系统会判断该订单的状态,不管是哪种状态都应给出对应的操作,这就是状态. 什么是状态模式? 在软 ...

  3. 设计模式之状态模式(State)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  4. 设计模式21:State 状态模式(行为型模式)

    State 状态模式(行为型模式) 动机(Motivation) 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态的行为就可能完全不同. ...

  5. php 状态模式,PHP设计模式(十九)—状态模式 (State Pattern)

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

  6. 【设计模式】状态模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

    文章目录 一.状态模式简介 二.状态模式适用场景 三.状态模式优缺点 四.状态模式相关设计模式 五.状态模式代码示例 1.状态类父类 2.播放状态类 3.暂停状态类 4.快进状态类 5.停止状态类 6 ...

  7. 策略模式和工厂模式的区别_java设计模式之状态模式,策略模式孪生兄弟

    状态模式 状态模式(State Pattern)中,类的行为是基于它的状态改变的,状态之间的切换,在状态A执行完毕后自己控制状态指向状态B,状态模式是不停的切换状态执行.这种类型的设计模式属于行为型模 ...

  8. python的编程模式-Python设计模式之状态模式原理与用法详解

    本文实例讲述了Python设计模式之状态模式原理与用法.分享给大家供大家参考,具体如下: 状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 ...

  9. 设计模式复习-状态模式

    #pragma once #include "stdafx.h"#include <queue> #include<iostream> using name ...

最新文章

  1. 好气啊,面试官不讲武德! | 每日趣闻
  2. HDU2091 空心三角形
  3. php 整数转换为32 位,PHP哈希函数返回一个整数(32位int)(PHP hashing function that returns an integer (32bit int))...
  4. ADO.NET知识汇总
  5. 把combobox控件添加到datagridview控件中_自定义系列:控件属性添加
  6. Java实现两个递增有序链表合并成一个递增有序链表和两个非递减有序链表合成一个非递增有序链表
  7. 日志级别_SpringBoot实战(十三):Admin动态修改日志级别
  8. 解决celipse中mybatis使用的时候xml没有提示的问题
  9. 【原】[webkit移动开发笔记]之空链接是使用javascript:void(0)还是使用#none
  10. SpringSecurity系列(四) Spring Security 实现权限树形菜单
  11. java基础-(一)-JDK的下载和安装
  12. ORACLE 树形遍历查询根节点 父节点 子节点
  13. Win10安装Ubuntu20.04双系统
  14. 【刷题】清橙 A1339 JZPLCM(顾昱洲)
  15. 计算机编码器的工作原理,优先级编码器74LS148的电路结构、工作原理及使用方法...
  16. 自用计算机一套送东西,购买笔记本电脑别忘记索要赠品
  17. python做三维图片挑战眼力_查找「儿童大家来找茬图片」安卓应用 - 豌豆荚
  18. 数据结构实验——用链表实现简单的多项式乘法
  19. 解决ueditor编辑器图片在线管理图片无法显示
  20. 教你操作视频横屏改竖屏的方法

热门文章

  1. Pandas输出文件使用Excel打开时中文出现乱码的解决方法
  2. 怎样提取音频制作手机铃声
  3. Lua封装增加九宫Sprite接口
  4. python验证码识别库底色深_基于机器学习的验证码识别
  5. Web 图集应用,属于非游戏类的图集/雪碧图对象/JS雪碧图/前端图集/JS图集
  6. 消息认证码和单向散列函数
  7. 自己搭服务器 做购物网站成本,从零搭建一个购物网站,实操经验
  8. 一个免费获得5年虚拟主机的方法
  9. BVT BAT (版本验证测试和版本验收测试)
  10. UNIX2DOS/DOS2UNIX for Windows