- state模式:允许一个对象在内部状态改变时,改变其行为
- Memento模式:保存原发器(Originator)对象的内部状态, 需要时恢复原发器的状态
状态变化模式
- 在组件构建过程中,有些状态经常变化
- 如何对这些变化进行有效的管理?
- 同时又维持高层模块的稳定?
- 典型模式
- State
- Memento
状态模式 State
- 某些对象的状态如果发生改变,其行为也会发生改变
- 文档处于只读状态,其行为与读写状态时不同
- 如何在运行时,根据对象的状态透明的改变对象的行为?
- 而不会在对象的状态变化和行为改变之间引入耦合?
模式定义
- 允许一个对象在其内部状态发生改变时改变其行为,使这个对象看上去就像改变了它的类型一样
- 如水一般,状态即事物所处的某一种形态。
- 状态模式是说一个对象在其内部状态发生改变时,其表现的行为和外在属性不一样,这个对象看上去就像改变了它的类型一样
- 因此,状态模式又称为对象的行为模式
- 状态模式的核心思想就是一个事物(对象)有多种状态
- 在不同的状态下所表现出来的行为和属性不一样
结构
初始设计
- 网络应用
- 根据网络状态做调整
- 在一个状态下执行操作后,会跨越到另一个状态
网络应用
enum NetworkState {
Network_Open,
Network_Close,
Network_Connect,
};
class NetworkProcessor{
NetworkState state;
public:
void Operation1(){
if (state == Network_Open){
//**********
state = Network_Close;
}
else if (state == Network_Close){
//..........
state = Network_Connect;
}
else if (state == Network_Connect){
//$$$$$$$$$$
state = Network_Open;
}
}
public void Operation2(){
if (state == Network_Open){
//**********
state = Network_Connect;
}
else if (state == Network_Close){
//.....
state = Network_Open;
}
else if (state == Network_Connect){
//$$$$$$$$$$
state = Network_Close;
}
}
public void Operation3(){
}
};
- 策略模式与这种情形很像
- 当枚举类型增加了新状态,就需要修改很多原来的代码
- 对修改封装
- 对扩展开放
重构
-
将枚举类型变成抽象基类
-
原来
enum NetworkState { Network_Open, Network_Close, Network_Connect, };
-
重构
class NetworkState{ }
-
-
把所有与状态相关的操作,都变成状态对象的行为
class NetworkState{ public: // 放一个状态对象的指针 NetworkState* pNext; virtual void Operation1()=0; virtual void Operation2()=0; virtual void Operation3()=0; virtual ~NetworkState(){} };
-
设计Open/Close/Connect态对象
class OpenState :public NetworkState{ static NetworkState* m_instance; public: // 单例:状态就一个,不需要多个状态 static NetworkState* getInstance(){ if (m_instance == nullptr) { m_instance = new OpenState(); } return m_instance; } void Operation1(){ //********** // 换成Close态时,换的是一个对象,而不是一个枚举 // 把跟状态相关的操作,都封装到一个状态对象中 pNext = CloseState::getInstance(); } void Operation2(){ //.......... pNext = ConnectState::getInstance(); } void Operation3(){ //$$$$$$$$$$ pNext = OpenState::getInstance(); } };
-
网络应用部分
class NetworkProcessor{ NetworkState* pState; public: NetworkProcessor(NetworkState* pState){ this->pState = pState; } void Operation1(){ //... /********************************** 虚函数的本质就是运行时的if..else.. 在运行时判断pState指向的是Open态的对象 就会调用Open对象的Ope1() **********************************/ pState->Operation1(); pState = pState->pNext; //... } void Operation2(){ //... pState->Operation2(); pState = pState->pNext; //... } void Operation3(){ //... pState->Operation3(); pState = pState->pNext; //... } };
-
优点
-
当状态增加的时候,只需增加一个状态类
class WaitState: public NetworkState {...}
-
而网络应用部分的代码不需要改变
- 因为状态行为内部自己管理状态的转变
- 应用部分不涉及状态改变
-
要点总结
- State模式把所有与一个特定状态相关的行为都放入一个State子类对象中
- 在对象状态切换时,切换对应的对象
- 同时维持State的接口
- 实现了具体操作(行为)与状态转换之间的解耦
- 为不同的转换引入不同的对象
- 使状态转换变得更加明确
- 而且可以保证不会出现状态不一致的情况
- 因为转换是原子性的
- 即要么彻底转换过来,要么不转换
- 如果State对象没有实例变量
- 各上下文可以共享(Singleton)同一个State对象
- 从而节省对象开销
Rust实现
- 用程序来模拟水的三种不同状态及相互转化
- 设置水的状态为液态、固态和气态
- 调用
water.behavior()
可以实现不同的动作
状态模式的类图
use std::collections::HashMap;
trait IState {
fn get_name(&self) -> &str;
fn is_match(&self, stateinfo: i32) -> bool;
fn behavior(&self);
}
struct SolidState {
name: String,
}
impl SolidState {
fn new() -> Self {
Self { name: "固态".to_string(), }
}
}
impl IState for SolidState {
fn get_name(&self) -> &str {
&self.name
}
fn behavior(&self) {
println!("[状态类]:{}", self.get_name());
}
fn is_match(&self, stateinfo: i32) -> bool {
return stateinfo <= 0;
}
}
struct LiquidState {
name: String,
}
impl LiquidState {
fn new() -> Self {
Self { name: "液态".to_string(), }
}
}
impl IState for LiquidState {
fn get_name(&self) -> &str {
&self.name
}
fn behavior(&self) {
println!("[状态类]:{}", self.get_name());
}
fn is_match(&self, stateinfo: i32) -> bool {
return stateinfo > 0 && stateinfo <= 100;
}
}
struct GasState {
name: String,
}
impl GasState {
fn new() -> Self {
Self { name: "气态".to_string(), }
}
}
impl IState for GasState {
fn get_name(&self) -> &str {
&self.name
}
fn behavior(&self) {
println!("[状态类]:{}", self.get_name());
}
fn is_match(&self, stateinfo: i32) -> bool {
return stateinfo > 100;
}
}
trait IContext {
fn get_state(&self) -> usize;
fn set_state(&mut self, state: usize);
fn set_stateinfo(&mut self, stateinfo: i32);
fn get_stateinfo(&self) -> i32;
}
struct Water {
// 使用HashMap来保存状态类,用于查询和删除
states: HashMap<usize, Box<dyn IState>>,
cur_state: usize,
stateinfo: i32,
}
impl Water {
fn new() -> Self {
Self {
states: HashMap::new(),
cur_state: 0,
stateinfo: 0,
}
}
fn init(&mut self) {
self.add_state(Box::new(SolidState::new()));
self.add_state(Box::new(LiquidState::new()));
self.add_state(Box::new(GasState::new()));
self.set_tmpr(-40);
}
fn add_state(&mut self, state: Box<dyn IState>) {
let len = self.states.len();
self.states.insert(len, state);
}
fn get_tmpr(&self) -> i32 {
self.get_stateinfo()
}
fn set_tmpr(&mut self, tmpr: i32) {
println!("[水元素] 设置水温:{} 度", tmpr);
self.set_stateinfo(tmpr);
}
fn rise_tmpr(&mut self, step: i32) {
self.set_stateinfo(self.get_tmpr() + step);
println!("[水元素] 升温至:{} 度", self.get_tmpr());
}
fn _reduce_tmpr(&mut self, step: i32) {
self.set_stateinfo(self.get_tmpr() - step)
}
fn behavior(&self) {
let state = self.get_state();
self.states.get(&state).as_ref().unwrap().behavior();
}
}
impl IContext for Water {
fn set_state(&mut self, idx: usize) {
let state = self.cur_state;
if self.cur_state == idx {
return;
}
println!(
"[IContext]: {} -> {}",
self.states.get(&state).as_ref().unwrap().get_name(),
self.states.get(&idx).as_ref().unwrap().get_name()
);
self.cur_state = idx;
}
fn get_state(&self) -> usize {
self.cur_state
}
fn set_stateinfo(&mut self, stateinfo: i32) {
self.stateinfo = stateinfo;
let mut state_idx = 0;
for (idx, state) in self.states.iter() {
if state.is_match(stateinfo) {
state_idx = *idx;
break;
}
}
self.set_state(state_idx);
}
fn get_stateinfo(&self) -> i32 {
self.stateinfo
}
}
fn main() {
let mut water = Water::new();
water.init();
for _ in 0..5 {
water.rise_tmpr(30);
water.behavior();
}
}
执行结果:
[水元素] 设置水温:-40 度
[水元素] 升温至:-10 度
[状态类]:固态
[IContext]: 固态 -> 液态
[水元素] 升温至:20 度
[状态类]:液态
[水元素] 升温至:50 度
[状态类]:液态
[水元素] 升温至:80 度
[状态类]:液态
[IContext]: 液态 -> 气态
[水元素] 升温至:110 度
[状态类]:气态
- 上面的应用场景是Water水元素
- 水元素实现了IContext接口,它关联(使用)了IState
- 水元素通过state.behavior()来执行状态类的行为
- IContext和IState都是固定不变的,可以单独设计
- 依赖它们的Water,SolidState/LiquidState是可变的,可以根据应用场景随时变化
Memento 备忘录模式
动机
- 某些对象在状态转换过程中
- 要求能回溯到对象之前的某个状态
- 如何实现对象状态的良好保存与恢复?
- 同时又不会破坏对象本身的封装?
模式定义
- 在不破坏封装的前提下
- 捕猎一个对象的内部状态
- 并在该对象之外保存这个状态
- 这样以后可以将该对象恢复到原先保存的状态
结构
初始设计
-
要保存对象状态的类
class Originator { string state; //.... 其他内部状态 };
- 需要给它拍一个内部的快照
-
设计另一个类:Memento
class Memento { string state; //.. 内部状态和 Originator 对应 public: Memento(const string & s) : state(s) {} string getState() const { return state; } void setState(const string & s) { state = s; } };
- 它内部状态和Originator是对应的
-
再给Originator增加Memento功能
class Originator { string state; //.... 其他内部状态 public: /****************************** * 创建了一个Memento的类 * 相当于给当前内存状态拍照 ******************************/ Originator() {} Memento createMomento() { Memento m(state); return m; } void setMomento(const Memento & m) { state = m.getState(); } };
-
客户端使用
int main() { Originator orginator; //捕获对象状态,存储到备忘录 Memento mem = orginator.createMomento(); //... 改变orginator状态 //从备忘录中恢复 orginator.setMomento(mem); }
要点总结
- 备忘录(Memento)保存原发器(Originator)对象的内部状态
- 需要时恢复原发器的状态
- Memento模式的核心是隐藏信息
- 即Originator要向外隐藏信息,保持其封装性
- 同时又要把状态保存到外界
- 现代语言都有序列化功能
- 往往采用效率高、容易正确使用的序列化方案来实现Memento