状态模式是一种行为型模式,让你能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。
它允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态模式与有限状态机的概念紧密相关。
其主要思想是程序在任意时刻仅可处于几种有限的状态中。在任何一个特定状态中,程序的行为都不相同,且可瞬间从一个状态切换到另一个状态。不过,根据当前状态,程序可能会切换到另外一种状态,也可能会保持当前状态不变。这些数量有限且预先定义的状态切换规则被称为转移。
你还可将该方法应用在对象上。假如你有一个文档Document
类。文档可能会处于草稿Draft
、审阅中Moderation
和已发布Published
三种状态中的一种。文档的publish发布
方法在不同状态下的行为略有不同:
草稿
状态时,它会将文档转移到审阅中状态。审阅中
状态时,如果当前用户是管理员,它会公开发布文档。已发布
状态时,它不会进行任何操作。状态机通常由众多条件运算符(if
或switch
)实现,可根据对象的当前状态选择相应的行为。 “状态” 通常只是对象中的一组成员变量值。即使你之前从未听说过有限状态机,你也很可能已经实现过状态模式。下面的代码应该能帮助你回忆起来。
12345678910111213141516 | class Document is field state: string // ... method publish() is switch (state) "draft": state = "moderation" break "moderation": if (currentUser.role == 'admin') state = "published" break "published": // 什么也不做。 break // ... |
---|
当我们逐步在文档
类中添加更多状态和依赖于状态的行为后,基于条件语句的状态机就会暴露其最大的弱点。为了能根据当前状态选择完成相应行为的方法,绝大部分方法中会包含复杂的条件语句。修改其转换逻辑可能会涉及到修改所有方法中的状态条件语句,导致代码的维护工作非常艰难。
这个问题会随着项目进行变得越发严重。我们很难在设计阶段预测到所有可能的状态和转换。随着时间推移,最初仅包含有限条件语句的简洁状态机可能会变成臃肿的一团乱麻。
将本来是针对对象的操作,委托给了这个对象的状态操作。运用状态模式可以将代码中的if,else代码块抽离出来,每个if,else下的代码块都封装为状态的某一个方法,所有状态都必须实现状态接口,即实现抽离出来的所有方法,由各自去执行在该状态下应该实现的逻辑,包括不合理的逻辑,可以抛出异常,这样可以规避大量的if,else嵌套。
状态模式建议为对象的所有可能状态新建一个类,然后将所有状态的对应行为抽取到这些类中。
原始对象被称为上下文(context),它并不会自行实现所有行为,而是会保存一个指向表示当前状态的状态对象的引用,且将所有与状态相关的工作委派给该对象。
如需将上下文转换为另外一种状态,则需将当前活动的状态对象替换为另外一个代表新状态的对象。采用这种方式是有前提的:所有状态类都必须遵循同样的接口,而且上下文必须仅通过接口与这些对象进行交互。
这个结构可能看上去与策略模式相似,但有一个关键性的不同————在状态模式中,特定状态知道其他所有状态的存在,且能触发从一个状态到另一个状态的转换;策略则几乎完全不知道其他策略的存在。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 | using System;namespace RefactoringGuru.DesignPatterns.State.Conceptual{ // The Context defines the interface of interest to clients. It also // maintains a reference to an instance of a State subclass, which // represents the current state of the Context. class Context { // A reference to the current state of the Context. private State _state = null; public Context(State state) { this.TransitionTo(state); } // The Context allows changing the State object at runtime. public void TransitionTo(State state) { Console.WriteLine($"Context: Transition to {state.GetType().Name}."); this._state = state; this._state.SetContext(this); } // The Context delegates part of its behavior to the current State // object. public void Request1() { this._state.Handle1(); } public void Request2() { this._state.Handle2(); } } // The base State class declares methods that all Concrete State should // implement and also provides a backreference to the Context object, // associated with the State. This backreference can be used by States to // transition the Context to another State. abstract class State { protected Context _context; public void SetContext(Context context) { this._context = context; } public abstract void Handle1(); public abstract void Handle2(); } // Concrete States implement various behaviors, associated with a state of // the Context. class ConcreteStateA : State { public override void Handle1() { Console.WriteLine("ConcreteStateA handles request1."); Console.WriteLine("ConcreteStateA wants to change the state of the context."); this._context.TransitionTo(new ConcreteStateB()); } public override void Handle2() { Console.WriteLine("ConcreteStateA handles request2."); } } class ConcreteStateB : State { public override void Handle1() { Console.Write("ConcreteStateB handles request1."); } public override void Handle2() { Console.WriteLine("ConcreteStateB handles request2."); Console.WriteLine("ConcreteStateB wants to change the state of the context."); this._context.TransitionTo(new ConcreteStateA()); } } class Program { static void Main(string[] args) { // The client code. var context = new Context(new ConcreteStateA()); context.Request1(); context.Request2(); } }} |
---|
执行结果:
1234567 | Context: Transition to ConcreteStateA.ConcreteStateA handles request1.ConcreteStateA wants to change the state of the context.Context: Transition to ConcreteStateB.ConcreteStateB handles request2.ConcreteStateB wants to change the state of the context.Context: Transition to ConcreteStateA. |
---|
参考原文:状态设计模式