状态模式
来源:互联网 发布: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开销,相应的内存开销就稍微大了一点。具体应用场景需要具体分析。
- 状态模式(状态变化)
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 状态模式
- 技术(5)—引用计数
- 约瑟夫环(joseph)
- Ubuntu 12.04忘记用户密码的解决办法
- 我与Qt不得不说的故事
- 关于ResourceLink导致Tomcat不能启动的问题
- 状态模式
- C++快速入门 (四) 引用 和 指针
- 学习使用github(一)
- 银行系统v1.1升级版(技术支持课)
- LNMP环境的搭建
- 电平触发方式和边沿触发的区别
- logcat信息
- 开发者应该开始学习 C++ 了
- 修改编译linux内核