状态模式

以下很多内容都来自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开销,相应的内存开销就稍微大了一点。具体应用场景需要具体分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值