C++实现Behavioral - State模式

【转载】 C++实现Behavioral - State模式
转载自: http://patmusing.blog.163.com/blog/static/13583496020101502024824/


也称为
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类图如下:

 C++实现Behavioral - State模式_第1张图片

考虑一个门的状态:

 C++实现Behavioral - State模式_第2张图片

说明:

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

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

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

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

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

// State.h

#include <iostream>

#include <string>

#include <typeinfo>

#include <memory>

using namespace 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类图如下:

 C++实现Behavioral - State模式_第3张图片

要点:

- 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



你可能感兴趣的:( C++实现Behavioral - State模式)