状态模式——当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式在我看来是我学习到目前设计模式之中最有意思的一个设计模式了,它给我的感觉就是绝对的高内聚,在表面层进行调用的时候,完全不知道它是怎么改变了状态的,我们只是给一个属性赋了值而已,然后调用了同样的方法,它的状态就完全变了。
一般的开发中,要根据一个状态去动态的输出,是需要很多的if-else的判断的,每增加一个状态,我们就需要在这个判断的类中进行修改,这样完全违背了单一责任原则和开发封闭原则。有了状态模式,我们可以很好的对代码进行分离,使每个状态都有自己的特点。
状态模式最重要的一个类是一个叫State的类,状态模式是通过把各种状态转移逻辑分布到State的子类之间,来减少互相的依赖。
1.State状态抽象类,提供一做事情的抽象方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 抽象状态 3 /// </summary> 4 public abstract class State 5 { 6 /// <summary> 7 /// 写代码的抽象方法 8 /// </summary> 9 /// <param name="work"></param> 10 public abstract void WriteProgram(Work work); 11 }
2.work类,提供一个或者多个改变状态的依据属性,构造方法实例化一个初始状态,并且定了一个状态类,后面提供一个改变状态的方法给状态子类去调用,同时提供一个运行当前状态子类的实际实现方法的方法,有点绕口,详细看代码,这边设计确实比较巧妙。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 工作类 3 /// </summary> 4 public class Work 5 { 6 /// <summary> 7 /// 定义一个当前状态 8 /// </summary> 9 private State _current; 10 11 /// <summary> 12 /// 构造方法设定当前方法为初始化,9点上班时的初始状态 13 /// 设置加班状态为false 14 /// </summary> 15 public Work() 16 { 17 TaskFinish = false; 18 _current=new ForenoonState(); 19 } 20 21 /// <summary> 22 /// 时间属性,状态转换的依据 23 /// </summary> 24 public double Hour { get; set; } 25 26 27 /// <summary> 28 /// 任务完成属性,是否下班的依据 29 /// </summary> 30 public bool TaskFinish { get; set; } 31 32 /// <summary> 33 /// 设定状态的方法 34 /// </summary> 35 /// <param name="state"></param> 36 public void SetState(State state) 37 { 38 _current = state; 39 } 40 41 /// <summary> 42 /// 调用当前状态写程序的方法 43 /// </summary> 44 public void WriteProgram() 45 { 46 _current.WriteProgram(this); 47 } 48 49 }
3.State的各种状态子类,都是根据work中的属性进行判断,是否是结束还是调用下一个状态。这种调用时连环调用的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class ForenoonState:State 2 { 3 public override void WriteProgram(Work work) 4 { 5 if(work.Hour<12) 6 { 7 Console.WriteLine("当前时间:{0}点 上午工作,精神百倍",work.Hour); 8 } 9 else 10 { 11 work.SetState(new NoonState()); 12 work.WriteProgram(); 13 } 14 } 15 } 16 17 public class NoonState:State 18 { 19 public override void WriteProgram(Work work) 20 { 21 22 if (work.Hour < 13) 23 { 24 Console.WriteLine("当前时间:{0}点 饿了午饭,犯困,午休", work.Hour); 25 } 26 else 27 { 28 work.SetState(new AfternoonState()); 29 work.WriteProgram(); 30 } 31 } 32 } 33 34 public class AfternoonState:State 35 { 36 public override void WriteProgram(Work work) 37 { 38 39 if (work.Hour < 17) 40 { 41 Console.WriteLine("当前时间:{0}点 下午状态还不错,继续努力", work.Hour); 42 } 43 else 44 { 45 work.SetState(new EveningState()); 46 work.WriteProgram(); 47 } 48 } 49 } 50 51 public class EveningState : State 52 { 53 public override void WriteProgram(Work work) 54 { 55 //加班状态为true时,进行休息状态 56 if (work.TaskFinish) 57 { 58 work.SetState(new RestState()); 59 work.WriteProgram(); 60 } 61 else 62 { 63 if (work.Hour < 21) 64 { 65 Console.WriteLine("当前时间:{0}点 加班哦,疲惫之极", work.Hour); 66 } 67 else 68 { 69 work.SetState(new SleepingState()); 70 work.WriteProgram(); 71 } 72 } 73 } 74 } 75 76 77 public class RestState:State 78 { 79 public override void WriteProgram(Work work) 80 { 81 Console.WriteLine("当前时间:{0}点 下班回家了", work.Hour); 82 } 83 } 84 85 public class SleepingState:State 86 { 87 public override void WriteProgram(Work work) 88 { 89 Console.WriteLine("当前时间:{0}点不行了,快睡着了!", work.Hour); 90 } 91 }
4.客户端代码,只需要定义改变状态的属性的值,然后调用实现方法,就可以让子类状态自己去找自己的实现方法了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var work = new Work(); 6 work.TaskFinish = true; 7 work.Hour = 9; 8 work.WriteProgram(); 9 10 work.Hour = 12; 11 work.WriteProgram(); 12 13 work.Hour = 14; 14 work.WriteProgram(); 15 16 work.Hour = 17; 17 work.WriteProgram(); 18 19 work.Hour = 19; 20 work.WriteProgram(); 21 22 work.Hour = 21; 23 work.WriteProgram(); 24 25 Console.ReadKey(); 26 } 27 }
当一个对象的行为取决于它的状态,并且它必须在运行时时刻根据状态改变它的行为时,就可以考虑使用状态模式了,当然,我觉得状态模式也有它的缺点,那就是实例化的对象有点多,是不是很占资源我不清楚,但是我感觉很占资源。
以上内容部分参考程杰的《大话设计模式》一书