文章目录
在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?状态变化为解决该问题提供了一解决方案。
1. State 状态模式
1.1 State 状态模式动机
在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态的行为就有可能完全不同。
那么如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?
1.2 模式定义
允许一个对象在其内部状态改变时改变它的行为(运用运行时多态,类的virtual函数)。从而使对象看起来似乎修改了其行为。
和 Strategy 策略模式 的形式类同,同时对于没有实例变量成员的状态类型,可以多个对象享有同一个状态对象,所以 Singleton 单例模式 也常常伴随使用。
1.3 实例代码
在网络编程中,经常涉及到接口编程,每个接口都有打开、关闭、连接等状态,每种状态对应着不同的行为,例子按此场景展开。
在 State1.cpp 中,又见到了熟悉的枚举类型和if-else结构,此时就能够想到 Strategy策略模式 的使用场景,将 枚举类型 NetworkStatue -> 抽象基类,再将枚举类型中的 枚举常量(Network_Open, Network_Close, Network_Connect) -> 抽象基类的派生子类(枚举类型的相关定义和知识见:enum枚举类型用法) ,重载抽象基类中所继承的 Operation() 操作函数。
再将客户对象 NetworkProcesser类定义中的 if-else结构去掉,新增一NetState抽象基类指针,在Operation()中使用该指针调用相应的operation()操作,即可实现 NetwordStatue 的行为随其中状态的变化而变化的模式定义,具体代码见 State2.cpp。
同时看到 NetworkState 中没有实例变量,所以对其使用单例模式可以提高程序性能,减少内存消耗。
|
|
1.4 要点总结
- State 模式将所有与一个特定状态相关的行为都放入一个 State的子类对象当中,在对象状态切换时,切换相应的对象;但同时维持 State 的接口,这样实现了 具体操作 和 状态转换 之间的解耦;
- 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——即要么彻底转换过来,要么不转换;
- 如果 State 对象没有实例变量,那么各个上下文可以共享同一个 State对象,从而节省对象开销;
2. Memento 备忘录模式
该模式现下已有更成熟更方便的框架工具来进行,所以只学习思想即可,不必拘泥于实现。
2.1 Memento 备忘录模式动机
在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的实现细节。
那么如何实现对象状态的良好保存和恢复?但同时又不会因此破坏对象本身的封装性?
2.2 模式定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
2.3 实例代码
class Memento {
string state;
//.. Memento对于Originator的记录不一定时完整备份,
//可以根据实际需要进行某些变量的快照即可
public:
Memento(const string & s) : state(s) {}
string getState() const { return state; }
void setState(const string & s) { state = s; }
};
class Originator {
string state;
//....
public:
Originator() {}
Memento createMomento() {
Memento m(state);
return m;
}
void setMomento(const Memento & m) {
state = m.getState();
}
};
int main() //caretaker / ClientApp
{
Originator orginator;
//捕获对象状态,存储到备忘录
Memento mem = orginator.createMomento();
//... //改变orginator状态
//从备忘录中恢复
orginator.setMomento(memento);
}
2.4 要点总结
- 备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态;
- Memento模式的核心时信息隐藏,即Originator需要向外界隐藏信息,保持其封装性。但同时有需要将状态保持到外界(Memento);
- 由于现代语言运行时(如C#,Java等)都具有相当的对象序列化支持,因此往往采用效率较高,又较容易正确实现的序列化方案来实现Memento模式;