状态模式

来源:互联网 发布:vidconvert mac注册机 编辑:程序博客网 时间:2024/05/18 23:54

以下很多内容都来自GOF的设计模式,我仅仅根据自己的理解进行了简化,方便大家的同时也便于后期的回忆


目的:

状态模式一般是用在一个可能会有多个状态的对象中。当此对象的内部状态改变的时候,它的行为也会改变。对象看起来似乎是修改了它的类。

动机:

考虑一个简单的tcp连接的对象TCPConnection,它可能会处于连接开启,连接关闭,连接建立三种状态。当一个TCPConnection对象收到其他对象的请求的时候,它根据自己当前的状态进行不同的反映。但是我总感觉这个不好理解,后面举的另一个例子我感觉不错,例如我们打开了一个pdf文档,当选择“手型工具”的时候,拖拽鼠标的时候,屏幕会跟着移动,当选择“选择工具”的时候,拖拽鼠标,会进行屏幕中文字的选择,当选择“高亮工具”的时候,拖拽鼠标,会对选择的文本进行高亮。针对选择的不同工具,我们可以认为其当前处于不同的状态,即处于不同的状态的时候,相同的鼠标拖拽操作会呈现不同的行为。

类图:

从上文的类图中可以看出,DrawController类作为客户类,它在调用MousePressed方法的时候,实际上调用的是currentTool的HandleMousePress方法,至于currentTool指的具体是哪个工具,可以通过给DrawController加一个方法SetTool来设定相应的工具。当用户点击“选择工具”按钮,就会调用DrawController的SetTool(new SelectionTool)方法,来设置currentTool属性。

再次强调:

当客户对象处于不同的状态,会改变相应操作的具体行为,如上图,客户选择了不同的工具,调用相同的MousePressed方法,却做了不同的事情。依据状态模式,将一个个的状态封装成具体的对象,如上面类图中的三个具体工具,同时继承一个抽象类。抽象类负责提供虚接口。

代码简单实现:

Head文件

#ifndef __PATTERN_STATE_H#define __PATTERN_STATE_H#include <stdio.h>#define LogPattern(fmt, ...) printf("[%4d %10s]" fmt"\n", __LINE__, __FUNCTION__, ##__VA_ARGS__ )//基类工具,默认空实现class GraphicTool{public:virtual void HandleMousePressed(){}protected:private:};//绘图控制器,根据不同的工具,控制鼠标的行为class DrawController{public:DrawController():m_pCurTool(0){}~DrawController(){}void MousePressed(){if(m_pCurTool)m_pCurTool->HandleMousePressed();}void SetTool(GraphicTool* tool);void Initialise();protected:private:GraphicTool* m_pCurTool;};//如果工具没有内部状态,可以设置其为单例模式,这样settool就不用频繁的new,delete了。//高亮工具class HighLightTool : public GraphicTool{public:void HandleMousePressed();protected:private:};//选择工具class SelectionTool : public GraphicTool{public:void HandleMousePressed();protected:private:};//文本工具class TextTool : public GraphicTool{public:void HandleMousePressed();protected:private:};#endif


Cpp文件

#include "PatternState.h"//初始化,设置默认工具.工具可以设置为单例模式void DrawController::Initialise(){SetTool(new TextTool());}void DrawController::SetTool(GraphicTool* tool){if (m_pCurTool){delete m_pCurTool;}m_pCurTool = tool;}void HighLightTool::HandleMousePressed(){LogPattern("\n");}void TextTool::HandleMousePressed(){LogPattern("\n");}void SelectionTool::HandleMousePressed(){LogPattern("\n");}



main文件的使用

void main(){DrawController draw;draw.Initialise();draw.MousePressed();draw.SetTool(new SelectionTool);draw.MousePressed();draw.SetTool(new HighLightTool);draw.MousePressed();}

截屏输出结果:




可以很清楚的看到,首先通过Initialise设置默认的工具为TextTool,然后调用MousePressed,实际上调用的为TextTool::HandleMousePressed

当通过draw.SetTool(new SelectionTool);设置工具为SelectionTool,再次调用MousePressed,实际上调用的为SelectionTool::HandleMousePressed

好的思想:

扩展:从上面可以看出,状态模式非常适合于扩展新的状态,只需要继承基类,实现对应的方法即可。

切换:关于状态的切换,上面的是在外部进行切换的,通过设置不同的工具来实现。事实上,还可以在状态类的内部实现。例如现在我想实现当用户选择了SelectionTool,选择之后立刻回到默认的TextTool,我们可以在客户调用MousePressed方法的时候,将客户类传入进去(即DrawController),在HandleMousePressed的末尾,调用controller->SetTool(new TextToll);即可以恢复到默认的TextTool。这样就实现了一种状态切换在内部实现的机制。

实现:关于状态类的实现,如果状态之间切换非常频繁,那么状态类可以使用单例模式,这样仅仅在第一次创建,省去了很多的new delete开销,相应的内存开销就稍微大了一点。具体应用场景需要具体分析。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 我儿子不爱说话怎么办 宝宝不爱学说话怎么办 宝宝误食了面霜怎么办 幼儿发烧不吃饭怎么办 宝宝发烧不吃饭怎么办 宝宝发烧不吃奶怎么办 新生儿用枕头了怎么办 新生儿宝宝奶睡怎么办 幼儿不爱吃水果怎么办 孩子不张个子怎么办 宝宝抗拒吃水果怎么办 夏天化妆爱出汗怎么办 上火嘴皮起泡怎么办 儿童拒绝吃水果怎么办 婴儿的牙长歪了怎么办 小孩吃东西不嚼怎么办 宝宝不爱吃苹果怎么办 宝宝吃猕猴桃拉肚子怎么办 宝宝吃猕猴桃过敏怎么办 怀孕了不想喝水怎么办 新生宝宝脾气大怎么办 一岁半宝宝痰多怎么办 孩子不吃水果蔬菜怎么办 宝宝感冒肚子响怎么办 大人不爱吃水果怎么办 孩子什么都不吃怎么办 儿童脾胃虚便秘怎么办 孩子不喜欢吃青菜怎么办 宝宝不喜欢吃青菜怎么办 儿童不爱吃水果怎么办 吃水果怕凉怎么办 受凉了发低烧怎么办 小孩受凉发低烧怎么办 出汗吹风发烧了怎么办 宝宝发烧闹人怎么办 感冒发烧出汗后怎么办 宝宝不爱吃蔬菜水果怎么办 小孩不爱吃蔬菜水果怎么办 一岁宝宝爱含饭怎么办 2岁宝宝爱含饭怎么办 吃药吃多了怎么办