状态变化模式
概念
定义
- 在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?
- 同时又维持高层模块的稳定?
- “状态变化”模式为这一问题提供了一种解决方案。
状态模式(State)
动机
- 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。
- 如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?
定义
- 每种状态独立封装,不同状态内部封装了不同行为。
- 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
UML 类图
角色
- 环境类(Context):定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
- 抽象状态类(State):定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
- 具体状态类(ConcreteState):实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
代码
// 抽象状态类
abstract class State {
public abstract Handle(context: Context): void;
}
// 具体状态A类
class ConcreteStateA extends State {
public Handle(context: Context) {
console.log('当前状态是 A.');
context.setState(new ConcreteStateB());
}
}
// 具体状态B类
class ConcreteStateB extends State {
public Handle(context: Context) {
console.log('当前状态是 B.');
context.setState(new ConcreteStateA());
}
}
// 环境类
class Context {
private state: State;
// 定义环境类的初始状态
constructor() {
this.state = new ConcreteStateA();
}
// 设置新状态
public setState(state: State) {
this.state = state;
}
// 读取状态
public getState(): State {
return this.state;
}
// 对请求做处理
public Handle() {
this.state.Handle(this);
}
}
class PatternClient {
public static main(args) {
// 创建环境
const context: Context = new Context();
// 处理请求
context.Handle();
context.Handle();
context.Handle();
context.Handle();
}
}
export default PatternClient;
要点总结
- State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
- 为不同的状态引入不同的对象,使得状态转化变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换时原子性的——即要么彻底转换过来,要么不转换。
- 如果State对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销。
备忘录模式(Memento)
动机
- 在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些共有的接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
- 如何实现对象状态的良好保存与恢复?同时又不会因此而破坏对象的本身的封装性?
模式定义
- 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态。
- 又叫快照模式。
- 除了创建它的发起人(originator)之外,其他对象都不能够访问这些状态信息。
UML 类图
角色
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
- 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
代码
// 备忘录
class Memento {
private state: string;
constructor(state: string) {
this.state = state;
}
public setState(state: string): void {
this.state = state;
}
public getState(): string {
return this.state;
}
}
// 发起人
class Originator {
private state: string;
public setState(state: string): void {
this.state = state;
}
public getState(): string {
return this.state;
}
public createMemento(): Memento {
return new Memento(this.state);
}
public restoreMemento(m: Memento): void {
this.setState(m.getState());
}
}
// 管理者
class Caretaker {
private memento: Memento;
public setMemento(m: Memento): void {
this.memento = m;
}
public getMemento(): Memento {
return this.memento;
}
}
class PatternClient {
public static main(args: Array<string>): void {
const or: Originator = new Originator();
const cr: Caretaker = new Caretaker();
or.setState('S0');
console.log('初始状态:' + or.getState());
cr.setMemento(or.createMemento()); // 保存状态
or.setState('S1');
console.log('新的状态:' + or.getState());
or.restoreMemento(cr.getMemento()); // 恢复状态
console.log('恢复状态:' + or.getState());
}
}
export default PatternClient;
要点总结
- 备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。
- Memento模式的核心是信息隐藏,即Originator需要向外界隐藏信息,保持其封装性。同时又需要将状态保持到外界(memento)。
- 由于语言运行时(c#,java等)都具有相当的对象序列化支持,因此往往采用效率较高、又较容易正确实现的序列化方案来实现Memento模式。