C++实现Behavioral - State模式

 

也称为Objects for States模式。

 

允许一个对象在其内部状态改变时改变其行为,从而使对象看起来似乎修改了类。

“Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.” – GoF

 

一个应用的行为往往会根据其内部变量值而表现出很大的不同。比如用Visual Studio开发程序,当写了一段程序后,如果点击保存的按钮,则所写的程序便保存到了硬盘上,然后保存按钮变灰,这时候程序文件时saved的状态;在这个状态上,如果有加了几行代码,那么状态就变成unsaved,此时,保存按钮又可以点击。savedunsaved是程序文件的状态,保存这个动作是行为,对于saved或者unsaved,同样的保存动作有不同的意义,即,如果程序文件时saved的状态,那么点击保存按钮,则什么也不做(按钮式灰色的);如果程序文件时unsaved状态,点击保存按钮,则保存程序文件。这个案例说明,对象的状态和其行为有密切的关系。

 

要解决类似上面的问题,通常的做法:

1.      设置一个状态变量

2.      switch – case或者if – else – if这样的办法来判定对象的状态,然后根据所得到的状态,选择不同行为

 

如果增加了一个状态,那么

1.      增加一个状态变量

2.      修改switch – case或者if – else – if语句,这显然不符合OCP的原则(这里会增加编译的时间成本),也没有把程序变化部分和相对稳定的部分隔离开,这会削弱应用程序的可扩展性

 

State模式就是为了解决上面的问题而产生的。即专门处理当对象处于不同的状态时,将会有不同的行为这样的情况。

State模式的UML类图如下:

 

考虑一个门的状态:

说明:

如果一个门处于Opened状态,那么对其施加close动作,就会使门的状态变成Closed的状态;

如果一个门处于Opened状态,那么对其施加open动作,那么门的状态不会改变,依旧是Opened的状态;

如果一个门处于Closed状态,那么对其施加open动作,就会使门的状态变成Opened的状态;

如果一个门处于Closed状态,那么对其施加close动作,那么门的状态不会改变,依旧是Closed的状态;

 

下面用State模式具体实现上面业务的C++代码:

// State.h

#include<iostream>

#include<string>

#include<typeinfo>

#include<memory>

usingnamespace std;

 

//前置声明

class Door;

 

//门的状态

class DoorState

{

public:

        virtual ~DoorState();

 

public:

        virtual void open(Door* door) = 0;              // 开门

        virtual void close(Door* door) = 0;             // 关门

};

 

//门打开状态

class DoorOpened :public DoorState

{

public:

        ~DoorOpened();

 

public:

        void open(Door* door);

        void close(Door* door);

};

 

//门关闭状态

class DoorClosed :public DoorState

{

public:

        ~DoorClosed();

 

public:

        void open(Door* door);

        void close(Door* door);

};

 

//

class Door

{

private:

        auto_ptr<DoorState> door_state;               //门的状态

 

public:

        Door();

        ~Door();

 

public:

        void open();                                                    // 开门

        void close();                                                    // 关门

        void set_state(auto_ptr<DoorState> door_state);              // 设置门的状态

        string get_state();                                                                      // 获取门的状态

};

 

// State.cpp

#include"State.h"

 

DoorState::~DoorState()

{

        cout << "in the destructor of DoorState..." << endl;

}

 

DoorOpened::~DoorOpened()

{

        cout << "in the destructor of DoorOpened..." << endl;

}

 

//如果门已经是打开的状态,则仅输出相关信息

void DoorOpened::open(Door* door)

{

        cout << "The Door is already opened. State will not change..." << endl;

}

 

//如果门是关闭的状态,则关门

void DoorOpened::close(Door* door)

{

        auto_ptr<DoorState> ds(new DoorClosed());

        door->set_state(ds);

}

 

DoorClosed::~DoorClosed()

{

        cout << "in the destructor of DoorClosed..." << endl;

}

 

//如果门是关闭的状态,则开门

void DoorClosed::open(Door* door)

{

        auto_ptr<DoorState> ds(new DoorOpened());

        door->set_state(ds);

}

 

//如果门已经是关闭的状态,则仅输出相关信息

void DoorClosed::close(Door* door)

{

        cout << "The Door is already closed. State will not change..." << endl;

}

 

Door::Door()

{

        auto_ptr<DoorState> ds(new DoorOpened());          // 设定门的初始状态

        door_state = ds;

}

 

Door::~Door()

{

        cout << "in the destructor of Door..." << endl;

}

 

void Door::open()                                                                      // 开门

{

        door_state->open(this);

}

 

void Door::close()                                                                      // 关门

{

        door_state->close(this);

}

 

void Door::set_state(auto_ptr<DoorState> door_state)    // 设定门的状态

{

        this->door_state = door_state;

}

 

//利用RTTI技术,输出门的状态

string Door::get_state()

{       // *(door_state.get())*(door_state)的写法是等价的,get()表示获取auto_ptr对象中underlying对象的真实指针,因此语

//法上显得更好理解一些。但*auto_ptr中也被重载过了,因此两者的结果是一样的。

        string temp = typeid(*(door_state.get())).name();     // temp将等于 "class DoorOpened""class DoorClosed"

        size_t found = temp.find("Door");                                  // 找到"Door"temp中位置

        temp = temp.substr(found + 4);                                     //去掉"Door"(包括其自身)前面的字符串

 

        return temp;

}

 

// PatternClient.cpp

#include"State.h"

 

int main(int argc,char** argv)

{

        Door door;

        cout << "1. Initial state of the door: " << door.get_state() << endl;

        cout << "------------------------------------------------------" << endl;

 

        cout << endl;

        door.open();

        cout << "2. State after opening the door: " << door.get_state() << endl;

        cout << "------------------------------------------------------" << endl;

 

        cout << endl;

        door.close();

        cout << "3. State after closing the door: " << door.get_state() << endl;

        cout << "------------------------------------------------------" << endl;

 

        cout << endl;

        door.close();

        cout << "4. State after closing the door: " << door.get_state() << endl;

        cout << "------------------------------------------------------" << endl;

 

        cout << endl;

        door.open();

        cout << "5. State after opening the door: " << door.get_state() << endl;

        cout << "------------------------------------------------------" << endl;

 

        cout << endl;

 

        return 0;

}

输出结果:

1. Initial state of the door: Opened

------------------------------------------------------

 

The Door is already opened. State will not change...

2. State after opening the door: Opened

------------------------------------------------------

 

in the destructor of DoorOpened...

in the destructor of DoorState...

3. State after closing the door: Closed

------------------------------------------------------

 

The Door is already closed. State will not change...

4. State after closing the door: Closed

------------------------------------------------------

 

in the destructor of DoorClosed...

in the destructor of DoorState...

5. State after opening the door: Opened

------------------------------------------------------

 

in the destructor of Door...

in the destructor of DoorOpened...

in the destructor of DoorState...

 

上面程序的UML类图如下:

 

要点:

-         State设计模式将一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口稳定,这样实现了具体操作与状态转换之间的解耦。

-         为不同的状态引入不同的对象,使得一个状态和一个对象一一对应,这也是为什么这个模式也称为Objects for States的原因。这使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为对象的状态是由一个类来表示的,那么就保证了状态转换的原子性即要么转换成功,要不转换 (即上面示例程序中的door_state要么被设定为DoorOpened,要么被设定为DoorClosed。如果一个对象有很多状态,采用通常的方法会有很复杂的if-else if-else语句,维护这些复杂的语句,很有可能导致各种麻烦,其中之一就是状态的不一致。)

-         State模式和Strategy模式很类似。它们最主要的区别就是:State模式是用不同的对象去封装不同的状态,而Stategy模式用不同的对象去封装不同的算法。

 

参考:

1.      auto_ptr的用途和原理,详见:http://patmusing.blog.163.com/blog/static/13583496020101824142699/

2.      使用auto_ptr的注意事项,详见:http://patmusing.blog.163.com/blog/static/13583496020101824541270/

3.      关于RTTI,详见Stan Lippman的《C++ Primer》第4版之第18

 

转:http://patmusing.blog.163.com/blog/static/13583496020101502024824/

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值