状态模式——水之三态

什么是状态?

状态即事物所处的某一种形态。状态模式是说一个对象在其内部状态发生改变时,其表现的行为和外在属性不一样,这个对象看上去就像是改变了它的类型一样。因此,状态模式又称为对象的行为模式。

如我们生活中经常见到的水,就有三种不同状态冰、水、水蒸汽,三种状态所表现的外在性质完全不一样:1.冰,质坚硬,无流动性,表面光滑;2.水,具有流动性;3.水蒸汽,肉眼看不见,却存在于空气中,质轻。这三种状态特性是不相差巨大?简直就不像是同一种物质的,但事实却是不管它是在什么状态,其特里组成都是一样的,都是水分子(H2O)。



以水为例说状态模式

你还记得初中物理学书上的这副图吗?水有三种状态:固、液、气,三种状态表现出不同的特性和行为,它们之间的转换也伴随着一着热力学的现象。现在要用程序来模拟水的三种状态和相互转换,要如何实现呢?


水之三态

我们从对象的角度来考虑会有哪个类,首先不管它是什么状态始终是水(H2O),所以会有一个Water类;而它又有三种状态,我们可以定义三个状态类:SolidState、LiquidState、GaseousState;从SolidState、LiquidState、GaseousState这三个单词中我们会发现都有一个State后缀,于是我们会想它们之间是否有一些共性,能否提取出一个更抽象的类,这个类就是状态类(State)。这些类之间的关系大致如下:


水与状态之间的类图关系

Ok,我们已经知道了大概的关系,那就开始Coding实现吧,在实现的过程中不断完善……

Water.h:

#ifndef __WATER_H__
#define __WATER_H__
//===============================================================
class State;
//===============================================================
class Water
{
public:Water();Water(State* pState, const string& strName);~Water();
public://改变状态bool ChangeState(State* pState);//设置温度void SetTemperature(int nTemperature);//获取温度int GetTemperature();//升温,nStep升高的幅度void RiseTemperature(int nStep);//降温,nStep降低的幅度void ReduceTemperature(int nStep);//获取名称string GetName();//水的作用(功效)void DoWork();
private:int         m_nTemperature;         //水温string      m_strName;              //名称State*      m_pState;               //状态
};
#endif  //__WATER_H__

State.h:

#ifndef __STATE_H__
#define __STATE_H__
//===============================================================
class Water;
//===============================================================
class State
{
public:virtual ~State() { }//某一状态下表现的行为,用途virtual void DoWork(Water* pWater) = 0;virtual string GetStateName();
protected:State(const string& strName) : m_strStateName(strName){}string m_strStateName;
};
class SolidState : public State
{
public:static SolidState* GetInstance();virtual void DoWork(Water* pWater);
private:SolidState(const string& strName) : State(strName) { }static SolidState* s_pSolidState;class CGarbo{public:~CGarbo(){if (SolidState::s_pSolidState != NULL){delete SolidState::s_pSolidState;}}};static CGarbo s_Carbo;
};
class LiquidState : public State
{
public:static LiquidState* GetInstance();virtual void DoWork(Water* pWater);
private:LiquidState(const string& strName) : State(strName) { }static LiquidState* s_pLiquidState;class CGarbo{public:~CGarbo(){if (LiquidState::s_pLiquidState != NULL){delete LiquidState::s_pLiquidState;}}};static CGarbo s_Carbo;
};
class GaseousState : public State
{
public:static GaseousState* GetInstance();virtual void DoWork(Water* pWater);
private:GaseousState(const string& strName) : State(strName){ }static GaseousState* s_pGaseousState;class CGarbo{public:~CGarbo(){if (GaseousState::s_pGaseousState != NULL){delete GaseousState::s_pGaseousState;}}};static CGarbo s_Carbo;
};
#endif  //__STATE_H__

Water.cpp:

#include "stdafx.h"
#include "Water.h"
#include "State.h"
Water::Water() : m_pState(NULL), m_strName("水(H2O)")
{SetTemperature(25);
}
Water::Water( State* pState, const string& strName) : m_nTemperature(0), m_pState(pState), m_strName(strName)
{if (m_pState->GetStateName().compare("固态") == 0){m_nTemperature = -25;} else if (m_pState->GetStateName().compare("液态") == 0){m_nTemperature = 25;} else if (m_pState->GetStateName().compare("气态") == 0){m_nTemperature = 125;}
}
Water::~Water()
{
}
bool Water::ChangeState( State* pState )
{if (!pState){return false;}if (!m_pState){cout << "初始化为" << pState->GetStateName() << endl;} else{cout << "由" << m_pState->GetStateName() << "变为" << pState->GetStateName() << endl;}m_pState = pState;return true;
}
void Water::SetTemperature( int nTemperature )
{m_nTemperature = nTemperature;if (m_nTemperature <= 0){ChangeState(SolidState::GetInstance());} else if (m_nTemperature > 0 && m_nTemperature <= 100){ChangeState(LiquidState::GetInstance());} else{ChangeState(GaseousState::GetInstance());}
}
int Water::GetTemperature()
{return m_nTemperature;
}
void Water::RiseTemperature( int nStep )
{SetTemperature(m_nTemperature + nStep);
}
void Water::ReduceTemperature( int nStep )
{SetTemperature(m_nTemperature - nStep);
}
std::string Water::GetName()
{return m_strName;
}
void Water::DoWork()
{if (m_pState){m_pState->DoWork(this);}
}

State.cpp:

#include "stdafx.h"
#include "State.h"
#include "Water.h"
string State::GetStateName()
{return m_strStateName;
}
//===============================================================
SolidState* SolidState::s_pSolidState = NULL;
LiquidState* LiquidState::s_pLiquidState = NULL;
GaseousState* GaseousState::s_pGaseousState = NULL;
//===============================================================
SolidState* SolidState::GetInstance()
{if (!s_pSolidState){s_pSolidState = new SolidState("固态");}return s_pSolidState;
}
void SolidState::DoWork( Water* pWater )
{cout << "我性格高冷,当前体温" << pWater->GetTemperature() << "摄氏度,"<< "我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……" << endl;
}
LiquidState* LiquidState::GetInstance()
{if (!s_pLiquidState){s_pLiquidState = new LiquidState("液态");}return s_pLiquidState;
}
void LiquidState::DoWork( Water* pWater )
{cout << "我性格温和,当前体温" << pWater->GetTemperature() << "摄氏度,"<< "我可滋润万物,饮用我可让你活力倍增……" << endl;
}
GaseousState* GaseousState::GetInstance()
{if (!s_pGaseousState){s_pGaseousState = new GaseousState("气态");}return s_pGaseousState;
}
void GaseousState::DoWork( Water* pWater )
{cout << "我性格热烈,当前体温" << pWater->GetTemperature() << "摄氏度,"<< "飞向天空是我毕生的梦想,在这你将看不到我的存在,我将达到无我的境界……" << endl;
}

测试代码:

void TestStateDesign()
{Water water;water.DoWork();water.SetTemperature(-4);water.DoWork();water.RiseTemperature(8);water.DoWork();water.RiseTemperature(110);water.DoWork();water.ReduceTemperature(130);water.DoWork();
}

结果如下:

初始化为液态
我性格温和,当前体温25摄氏度,我可滋润万物,饮用我可让你活力倍增……
由液态变为固态
我性格高冷,当前体温-4摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……
由固态变为液态
我性格温和,当前体温4摄氏度,我可滋润万物,饮用我可让你活力倍增……
由液态变为气态
我性格热烈,当前体温114摄氏度,飞向天空是我毕生的梦想,在这你将看不到我的存在,
我将达到无我的境界……
由气态变为固态
我性格高冷,当前体温-16摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……

好了,上面的程序已经完美地帮我们完成了水的三种状态及其转变的模拟。我们再来整理一个上面的代码中的类图结构,如下图:


水与状态之间的类图关系

SolidState、LiquidState、GaseousState三个类用了单例的模式,因为状态只需要一个对象就可以了,这三个类中CGarbo是一个内部类,只为了释放静态对象,关于单例模式的用法,可以参考《生活中的单例——不是单身》。Water中的ChangeState可方便地进行三种状态之间的转换。

水之三态升级版

在本篇文章发表之后,有读者提出说上面的例子并不太好:因为void Water::SetTemperature( int nTemperature )方法不符合程序设计中的开放封闭原则,如果再加一个状态(State),则要在SetTemperature中再一个if else判断。

这位读者说的确实有道理,这里的SetTemperature确实不符合开放封闭原则,在这要感谢[reply]FRcheng[/reply]提出了宝贵的意见和对此的看法。但我依然觉得水的三种状态是状态模式的典型,因为这特别贴近生活,一说就懂。会出现不符合开放封闭原则的情况是因为我程序写的完善,于是经过思考后又修改了一个新的版本,这版本即符合开发封闭原则,又满足了FRcheng提到的“状态可变化,也可增加”的需求。

State.h:

#ifndef __STATE_H__
#define __STATE_H__//===============================================================
class Water;
//===============================================================class State
{
public:virtual ~State() { }virtual string GetStateName();//某一状态下表现的行为,用途virtual void DoWork(Water* pWater) = 0;//返回这一状态下的默认温度virtual int GetDefaultTemperature() = 0;//指定的温度是否符合当前的状态virtual bool IsMatched(int nTemperature) = 0;protected:State(const string& strName) : m_strStateName(strName){}string m_strStateName;
};class SolidState : public State
{
public:static SolidState* GetInstance();virtual void DoWork(Water* pWater);virtual int GetDefaultTemperature();virtual bool IsMatched(int nTemperature);private:SolidState(const string& strName) : State(strName) { }static SolidState* s_pSolidState;class CGarbo{public:~CGarbo(){if (SolidState::s_pSolidState != NULL){delete SolidState::s_pSolidState;}}};static CGarbo s_Carbo;
};class LiquidState : public State
{
public:static LiquidState* GetInstance();virtual void DoWork(Water* pWater);virtual int GetDefaultTemperature();virtual bool IsMatched(int nTemperature);private:LiquidState(const string& strName) : State(strName) { }static LiquidState* s_pLiquidState;class CGarbo{public:~CGarbo(){if (LiquidState::s_pLiquidState != NULL){delete LiquidState::s_pLiquidState;}}};static CGarbo s_Carbo;
};class GaseousState : public State
{
public:static GaseousState* GetInstance();virtual void DoWork(Water* pWater);virtual int GetDefaultTemperature();virtual bool IsMatched(int nTemperature);private:GaseousState(const string& strName) : State(strName){ }static GaseousState* s_pGaseousState;class CGarbo{public:~CGarbo(){if (GaseousState::s_pGaseousState != NULL){delete GaseousState::s_pGaseousState;}}};static CGarbo s_Carbo;
};#endif  //__STATE_H__

State.cpp:

#include "stdafx.h"
#include "State.h"
#include "Water.h"string State::GetStateName()
{return m_strStateName;
}//===============================================================
SolidState* SolidState::s_pSolidState = NULL;
LiquidState* LiquidState::s_pLiquidState = NULL;
GaseousState* GaseousState::s_pGaseousState = NULL;
//===============================================================SolidState* SolidState::GetInstance()
{if (!s_pSolidState){s_pSolidState = new SolidState("固态");}return s_pSolidState;
}void SolidState::DoWork( Water* pWater )
{cout << "我性格高冷,当前体温" << pWater->GetTemperature() << "摄氏度," << "我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……" << endl;
}int SolidState::GetDefaultTemperature()
{return -5;
}bool SolidState::IsMatched( int nTemperature )
{return nTemperature < 0;
}LiquidState* LiquidState::GetInstance()
{if (!s_pLiquidState){s_pLiquidState = new LiquidState("液态");}return s_pLiquidState;
}void LiquidState::DoWork( Water* pWater )
{cout << "我性格温和,当前体温" << pWater->GetTemperature() << "摄氏度," << "我可滋润万物,饮用我可让你活力倍增……" << endl;
}int LiquidState::GetDefaultTemperature()
{return 25;  //常温
}bool LiquidState::IsMatched( int nTemperature )
{return nTemperature >= 0 && nTemperature < 100;
}GaseousState* GaseousState::GetInstance()
{if (!s_pGaseousState){s_pGaseousState = new GaseousState("气态");}return s_pGaseousState;
}void GaseousState::DoWork( Water* pWater )
{cout << "我性格热烈,当前体温" << pWater->GetTemperature() << "摄氏度," << "飞向天空是我毕生的梦想,在这你将看不到我的存在,我将达到无我的境界……" << endl;
}int GaseousState::GetDefaultTemperature()
{return 105;
}bool GaseousState::IsMatched( int nTemperature )
{return nTemperature >= 100;
}

Water.h:

#ifndef __WATER_H__
#define __WATER_H__//===============================================================
class State;
//===============================================================
#include <vector>
//===============================================================class Water
{
public:Water();Water(State* pState, const string& strName);~Water();public:void AddState(State* pState);//改变状态bool ChangeState(State* pState);//设置温度void SetTemperature(int nTemperature);//获取温度int GetTemperature();//升温,nStep升高的幅度void RiseTemperature(int nStep);//降温,nStep降低的幅度void ReduceTemperature(int nStep);//获取名称string GetName();//水的作用(功效)void DoWork();private:int                     m_nTemperature;     //水温string                  m_strName;          //名称State*                  m_pCurrentState;    //当前状态typedef vector<State*>  StateVec;StateVec*               m_pvecState;        //所有可能的状态
};
#endif  //__WATER_H__

Water.cpp:

#include "stdafx.h"
#include "Water.h"
#include "State.h"
#include <algorithm>Water::Water() : m_pCurrentState(LiquidState::GetInstance()), m_strName("水(H2O)"),m_pvecState(new StateVec())
{m_pvecState->push_back(m_pCurrentState);m_nTemperature = m_pCurrentState->GetDefaultTemperature();
}Water::Water( State* pState, const string& strName) : m_nTemperature(0), m_pCurrentState(pState),m_strName(strName), m_pvecState(new StateVec())
{m_pvecState->push_back(m_pCurrentState);m_nTemperature = m_pCurrentState->GetDefaultTemperature();
}Water::~Water()
{m_pCurrentState = NULL;m_pvecState->clear();delete m_pvecState;m_pvecState = NULL;
}void Water::AddState( State* pState )
{StateVec::iterator itrResult = find(m_pvecState->begin(), m_pvecState->end(), pState);if (itrResult == m_pvecState->end()){m_pvecState->push_back(pState); //如果不在状态列表中,则添加进状态列表}
}bool Water::ChangeState( State* pState )
{if (!pState){return false;}if (!m_pCurrentState){cout << "初始化为" << pState->GetStateName() << endl;} else{cout << "由" << m_pCurrentState->GetStateName() << "变为" << pState->GetStateName() << endl;}m_pCurrentState = pState;StateVec::iterator itrResult = find(m_pvecState->begin(), m_pvecState->end(), pState);if (itrResult == m_pvecState->end()){m_pvecState->push_back(pState); //如果不在状态列表中,则添加进状态列表}return true;
}void Water::SetTemperature( int nTemperature )
{m_nTemperature = nTemperature;//如果此温度不符合当前的状态,则要更改状态if (!m_pCurrentState->IsMatched(m_nTemperature)){for (StateVec::iterator itr = m_pvecState->begin(); itr != m_pvecState->end(); ++ itr){State* pState = *itr;if (pState->IsMatched(m_nTemperature)){ChangeState(pState);}}}
}int Water::GetTemperature()
{return m_nTemperature;
}void Water::RiseTemperature( int nStep )
{SetTemperature(m_nTemperature + nStep);
}void Water::ReduceTemperature( int nStep )
{SetTemperature(m_nTemperature - nStep);
}std::string Water::GetName()
{return m_strName;
}void Water::DoWork()
{if (m_pCurrentState){m_pCurrentState->DoWork(this);}
}

测试代码:

void TestStateDesign()
{Water water;water.AddState(SolidState::GetInstance());water.AddState(LiquidState::GetInstance());water.AddState(GaseousState::GetInstance());water.DoWork();water.SetTemperature(-4);water.DoWork();water.RiseTemperature(8);water.DoWork();water.RiseTemperature(110);water.DoWork();water.ReduceTemperature(130);water.DoWork();
}

结果:

我性格温和,当前体温25摄氏度,我可滋润万物,饮用我可让你活力倍增……
由液态变为固态
我性格高冷,当前体温-4摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……
由固态变为液态
我性格温和,当前体温4摄氏度,我可滋润万物,饮用我可让你活力倍增……
由液态变为气态
我性格热烈,当前体温114摄氏度,飞向天空是我毕生的梦想,在这你将看不到我的存在,
我将达到无我的境界……
由气态变为固态
我性格高冷,当前体温-16摄氏度,我坚如钢铁,仿如一冷血动物,请用我砸人,嘿嘿……


状态模式总结

通过上面一个生活中的例子,应该很容易能明白状态模式吧!其实也简单,其结构关系如下:


状态模式结构图

Behavior是不同一个对象在不同状态下的行为(如DoWork),ChangeState用于改变对象的状态,Request则是操作请求(如RiseTemperature和ReduceTemperature)。

状态模式——水之三态相关推荐

  1. 第03课:生活中的状态模式——人与水的三态

    用程序来模拟生活 从剧情中思考状态模式 状态模式的模型抽象 代码框架 类图 基于框架的实现 模型说明 应用场景 [故事剧情] 一个天气晴朗的周末,Tony 想去图书馆给自己充充电.于是背了一个双肩包, ...

  2. JS设计模式(13)状态模式

    什么是状态模式? 定义:将事物内部的每个状态分别封装成类,内部状态改变会产生不同行为. 主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为. 何时使用:代码中包含大 ...

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

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

  4. 设计模式:状态模式(State Pattern)

    作者:Wang Juqiang  创建于:2012-07-16 出处:http://www.cnblogs.com/wangjq/archive/2012/07/16/2593485.html 收录于 ...

  5. else 策略模式去掉if_设计模式(三)——简单的状态模式代替if-else

    博主将会针对Java面试题写一组文章,包括J2ee,SQL,主流Web框架,中间件等面试过程中面试官经常问的问题,欢迎大家关注.一起学习,一起成长. 前言 大多数开发人员现在还在使用if else的过 ...

  6. 【设计模式】 模式PK:策略模式VS状态模式

    1.概述 行为类设计模式中,状态模式和策略模式是亲兄弟,两者非常相似,我们先看看两者的通用类图,把两者放在一起比较一下. 策略模式(左)和状态模式(右)的通用类图. 两个类图非常相似,都是通过Cont ...

  7. Python设计模式-状态模式

    Python设计模式-状态模式 代码基于3.5.2,代码如下; #coding:utf-8 #状态模式class state():def writeProgram(self,work):raise N ...

  8. 18State(状态)模式

    技术交流QQ群:1027579432,欢迎你的加入! 1.状态变化模式 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?状态变化模式为这一问题提供 ...

  9. Java设计模式之策略模式与状态模式

    一.策略模式定义 定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们之间可以相互替换,策略模式可以在不影响客户端的情况下发生变化. 好了,定义看看就完了,我知道你很烦看定义. 二.策 ...

最新文章

  1. left join 和 left outer join 的区别
  2. U型管铁球或将代替现代火箭喷射装置
  3. Verilog自编函数clog2替代SV中的系统函数$clog2
  4. 学习方法之02掌握记忆方法,在学习上就赢了一半
  5. 1030利用三层交换机实现VLAN间通信
  6. (转)Spring Boot (十三): Spring Boot 小技巧
  7. php 生成等比例缩略图,PHP实现原比例生成缩略图的方法
  8. icd植入是大手术吗_母狗绝育是大手术吗?手术完需要住院吗?绝育后是不是会变胖?...
  9. 【网络工程师配置篇】华三交换机基本配置大全(文末附下载链接),网络工程师必备手册
  10. poi实现多个excel合并成一个excel
  11. 一篇文章教你搞懂日志采集利器 Filebeat
  12. Advanced Computer Network Review(4)——Congestion Control of MPTCP
  13. 青岛科技大学和青岛大学计算机专业,青岛科技大学和青岛大学企业管理专业哪个比较好考...
  14. MFCXTP库读取和写入XML文件
  15. 数位dp算法——洛谷p1980
  16. 【ARC101E】Ribbons on Tree(树形DP,容斥原理)
  17. 拓扑排序基本题目(一) OpenJ_Bailian - 4084
  18. 雨听 | 解决连接蓝牙后谷歌浏览器无声音(其他应用有声音)问题
  19. web服务器/app应用服务器
  20. 【Python】PyQT5+爬虫实现简单音乐下载器

热门文章

  1. 【雅思阅读】王希伟阅读P4(matching2段落信息配对题【困难】)
  2. 【计算机网络】——习题解析:UDP 用户数据报的首部十六进制表示是:06 32 00 45 00 1C E2 17,试求源端口、目的端口、 用户数据报的总长度、数据部分长度等
  3. Linux设备驱动开发详解【二】_设备驱动相关硬件基础知识
  4. Delphi 10.2 Tokyo 下载激活
  5. Re6:读论文 LeSICiN: A Heterogeneous Graph-based Approach for Automatic Legal Statute Identification fro
  6. mysql.proc is wrong_解决警告:数据库错误Column count of mysql.proc is wrong.
  7. 【机器学习】深度解析机器学习五大流派中主算法精髓
  8. 如何在 CentOS Stream 8上安装 LibreNMS
  9. ZQOJ 1123: 最佳校友
  10. 华三s3100v3时区配置_H3C S3100 交换机配置步骤