[Head First设计模式]生活中学设计模式——状态模式
系列文章
[Head First设计模式]山西面馆中的设计模式——装饰者模式
[Head First设计模式]山西面馆中的设计模式——观察者模式
[Head First设计模式]山西面馆中的设计模式——建造者模式
[Head First设计模式]饺子馆(冬至)中的设计模式——工厂模式
[Head First设计模式]抢票中的设计模式——代理模式
[Head First设计模式]云南米线馆中的设计模式——模版方法模式
[Head First设计模式]餐馆中的设计模式——命令模式
[Head First设计模式]身边的设计模式——适配器模式
[Head First设计模式]生活中学设计模式——迭代器模式
[Head First设计模式]生活中学设计模式——组合模式
[Head First设计模式]生活中学设计模式——外观模式
引言
状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。
允许一个对象在其内部状态改变时改变它的行为。
状态模式
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
类图
Context:状态管理器,它定义了客户感兴趣的接口,这个接口是由State去实现的;并维护一个ConcreteState子类的实例,这个实例定义当前的状态,当前状态变化的时候,会将变化后的ConcreteState返回给当前状态。
角色
state:状态,定义一个接口以封装与Context的特定状态相关的行为,但是行为本身是空的,该行为是由ConcreteState来实现的。
ConcreteState:具体状态子类,每一个子类实现一个与Contex的一个状态相关的行为。
一个例子
在过马路的时候,交通灯的状态,红,黄,绿,时间到了,在三个状态中切换。现在就用代码进行模拟。
1 namespace Wolfy.状态模式 2 { 3 /// <summary> 4 /// 抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为 5 /// </summary> 6 public abstract class TrafficLightState 7 { 8 /// <summary> 9 /// 改变状态的方法 10 /// </summary> 11 public abstract void ChangeState(TrafficLight context); 12 } 13 }
1 namespace Wolfy.状态模式 2 { 3 /// <summary> 4 /// 具体状态类 绿灯状态 5 /// </summary> 6 public class GreenState:TrafficLightState 7 { 8 public override void ChangeState(TrafficLight contex) 9 { 10 Console.WriteLine("绿灯行"); 11 contex.SetState(new RedState()); 12 } 13 } 14 }
1 namespace Wolfy.状态模式 2 { 3 /// <summary> 4 /// 具体状态类 黄灯状态 5 /// </summary> 6 public class YellowState : TrafficLightState 7 { 8 9 public override void ChangeState(TrafficLight contex) 10 { 11 Console.WriteLine("黄灯亮了等一等"); 12 //改变状态 13 contex.SetState(new GreenState()); 14 } 15 } 16 }
1 namespace Wolfy.状态模式 2 { 3 /// <summary> 4 /// 具体状态类 红灯状态 5 /// </summary> 6 public class RedState : TrafficLightState 7 { 8 public override void ChangeState(TrafficLight contex) 9 { 10 Console.WriteLine("红灯停"); 11 //改变状态 12 contex.SetState(new YellowState()); 13 } 14 } 15 }
1 namespace Wolfy.状态模式 2 { 3 /// <summary> 4 ///TrafficLight相当于 Context(上下文)类 拥有一些内部状态 5 /// </summary> 6 public class TrafficLight 7 { 8 //定时器 为了模拟等待的过程 定义一个定时器 9 private Timer timer; 10 //记录状态 11 private TrafficLightState state; 12 /// <summary> 13 /// 构造函数 14 /// </summary> 15 /// <param name="time">时间间隔</param> 16 public TrafficLight(int time) 17 { 18 this.timer = new Timer(); 19 this.timer.Start(); 20 this.timer.Elapsed += timer_Elapsed; 21 this.timer.Interval = time; 22 23 } 24 void timer_Elapsed(object sender, ElapsedEventArgs e) 25 { 26 this.Request(); 27 } 28 public void SetState(TrafficLightState state) 29 { 30 this.state = state; 31 } 32 /// <summary> 33 /// 对请求做处理,并设置下一个状态 34 /// </summary> 35 public void Request() 36 { 37 //时间到 改变一次状态 38 state.ChangeState(this); 39 } 40 } 41 }
测试
1 namespace Wolfy.状态模式 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 TrafficLight trafficLight = new TrafficLight(10000); 8 //初始化第一个状态 9 trafficLight.SetState(new RedState()); 10 //发送请求 显示第一个状态 不用等待 11 trafficLight.Request(); 12 Console.Read(); 13 } 14 } 15 }
结果
总结
优点
状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开。
State模式将所有与一个特定的状态相关的行为都放入一个对象中。因为所有与状态相关的代码都存在于某一个State子类中, 所以通过定义新的子类可以很容易的增加新的状态和转换。另一个方法是使用数据值定义内部状态并且让 Context操作来显式地检查这些数据。但这样将会使整个Context的实现中遍布看起来很相似的条件if else语句或switch case语句。增加一个新的状态可能需要改变若干个操作, 这就使得维护变得复杂了。State模式避免了这个问题, 但可能会引入另一个问题, 因为该模式将不同状态的行为分布在多个State子类中。这就增加了子类的数目,相对于单个类的实现来说不够紧凑。但是如果有许多状态时这样的分布实际上更好一些, 否则需要使用巨大的条件语句。正如很长的过程一样,巨大的条件语句是不受欢迎的。它们形成一大整块并且使得代码不够清晰,这又使得它们难以修改和扩展。 State模式提供了一个更好的方法来组织与特定状态相关的代码。决定状态转移的逻辑不在单块的 i f或s w i t c h语句中, 而是分布在State子类之间。将每一个状态转换和动作封装到一个类中,就把着眼点从执行状态提高到整个对象的状态。这将使代码结构化并使其意图更加清晰。
它使得状态转换显式化
当一个对象仅以内部数据值来定义当前状态时 , 其状态仅表现为对一些变量的赋值,这不够明确。为不同的状态引入独立的对象使得转换变得更加明确。而且, State对象可保证Context不会发生内部状态不一致的情况,因为从 Context的角度看,状态转换是原子的—只需重新绑定一个变量(即Context的State对象变量),而无需为多个变量赋值
State对象可被共享
如果State对象没有实例变量—即它们表示的状态完全以它们的类型来编码—那么各Context对象可以共享一个State对象。当状态以这种方式被共享时, 它们必然是没有内部状态, 只有行为的轻量级对象。
缺点
导致较多的ConcreteState子类。
适用场景
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。
一个操作中含有庞大的分支机构,并且这些分支决定于对象的状态。
参考
《Head First 设计模式》