今天我们来将状态模式,首先,我们来描述下面一个场景:
一、案例:
在工作过程中,根据时间段的不同,我们工作的状态也有所不同,下面,我们用简单的控制台应用程序,来实现一下这个场景。
1 public static int Hour = 0;//钟点 2 private static bool WorkFinished = false;//任务完成标记 3 4 public static void WriteProgram() 5 { 6 if (Hour < 12) 7 { 8 Console.WriteLine($"当前时间:{Hour}点,上午工作,状态好"); 9 } 10 else if (Hour < 13) 11 { 12 Console.WriteLine($"当前时间:{Hour}点,午饭 犯困 午休"); 13 } 14 else if (Hour<17) 15 { 16 Console.WriteLine($"当前时间:{Hour}点,下午工作,状态一般"); 17 } 18 else 19 { 20 if (WorkFinished) 21 { 22 Console.WriteLine($"当前时间:{Hour}点,下班回家了。"); 23 } 24 else 25 { 26 Console.WriteLine($"当前时间:{Hour}点,加班哦。"); 27 } 28 } 29 }
客户端
1 public static void Main() 2 { 3 Hour = 9; 4 WriteProgram(); 5 Hour = 12; 6 WriteProgram(); 7 Hour = 13; 8 WriteProgram(); 9 Hour = 14; 10 WriteProgram(); 11 Hour = 17; 12 WriteProgram(); 13 Hour = 18; 14 WriteProgram(); 15 Console.ReadKey(); 16 }
二、演绎
1、第一步演绎
看到上面用代码描述的场景,对于我们学了好多设计模式的小伙伴来讲,是不是显得特别的挫,最起码,我们也要抽象出一个类来呀。好,下面我们稍微改进一下代码:
1 public class Work 2 { 3 private int hour; 4 5 public int Hour 6 { 7 get 8 { 9 return hour; 10 } 11 12 set 13 { 14 hour = value; 15 } 16 } 17 18 public bool Finish 19 { 20 get 21 { 22 return finish; 23 } 24 25 set 26 { 27 finish = value; 28 } 29 } 30 31 private bool finish = false; 32 33 public void WriteProgram() 34 { 35 if ( hour< 12) 36 { 37 Console.WriteLine($"当前时间:{hour}点,上午工作,状态好"); 38 } 39 else if (hour < 13) 40 { 41 Console.WriteLine($"当前时间:{hour}点,午饭 犯困 午休"); 42 } 43 else if (hour < 17) 44 { 45 Console.WriteLine($"当前时间:{hour}点,下午工作,状态一般"); 46 } 47 else 48 { 49 if (finish) 50 { 51 Console.WriteLine($"当前时间:{hour}点,下班回家了。"); 52 } 53 else 54 { 55 Console.WriteLine($"当前时间:{hour}点,加班哦。"); 56 } 57 } 58 } 59 }
客户端:
1 public static void Main() 2 { 3 Work emergencyProjects = new Work(); 4 emergencyProjects.Hour = 9; 5 emergencyProjects.WriteProgram(); 6 emergencyProjects.Hour = 13; 7 emergencyProjects.WriteProgram(); 8 emergencyProjects.Hour = 16; 9 emergencyProjects.WriteProgram(); 10 emergencyProjects.Hour = 18; 11 emergencyProjects.WriteProgram(); 12 Console.ReadKey(); 13 }
上述代码,我们将工作写了一个Work类出来,让客户端来调用。
2、第二步演绎
那么我们看看我们改过之后的代码有什么问题呢?请看WriteProgram()方法,现在是这些状态分支,如果有更多的状态分支,那么这个方法将会很长,一个方法一大串代码。
如果一个方法代码过长,极有可能坏了味道。
我们的WriteProgram()方法中有很多判断的分支,这意味着它的责任过大了。无论任何状态,都需要通过它来改变,这其实是非常糟糕的。
面向对象设计其实就是希望做到代码责任的分解,我们的Work类其实违背了“单一职责”的原则。
比如WriteProgram()这个方法,判断这么多,任何的增加和减少,都需要修改这个方法,一不留神,可能会将其他的判断分支给修改了,这样对整个代码的维护风险是非常大的。
说白了,这也违反了“开放-封闭”原则。
通过上述的分析,我们可能会想到:将判断的每一个分支都写成一个一个的类,增加分支就相当于增加类,这样也不会影响到其他的类。
理论想的很好,但是如何实现呢?那就是我们今天要讲的状态模式。
状态模式:一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决:当控制一个对象状态转换的条件过于复杂时的情况,把状态的判断逻辑转移到不同状态的一系列类中去。可以把复杂的逻辑判断简化。当然,如果这个判断非常的简单,就没有必要用“状态模式”了。
好下面我们来看一下状态模式的结构
1 //封装一个与Context的一个特定状态相关的行为 2 abstract class State 3 { 4 public abstract void Handle(Context context); 5 } 6 //维护一个ConCreteState子类的实例,这个实例定义当前的状态 7 class Context 8 { 9 private State state; 10 11 public Context(State state)//定义Context的初始状态 12 { 13 this.State = state; 14 } 15 //可读写的状态属性,用于读取当前状态和设置当前状态 16 internal State State 17 { 18 get 19 { 20 return state; 21 } 22 23 set 24 { 25 state = value; 26 Console.WriteLine($"当前状态{state.GetType().Name}"); 27 } 28 } 29 //对请求做处理,并设置下一状态 30 public void Request() 31 { 32 state.Handle(this); 33 } 34 } 35 /// <summary> 36 /// 具体的State类,实现一个与Context的一个状态相关的行为 37 /// </summary> 38 class ConcreteStateA:State 39 { 40 public override void Handle(Context context) 41 { 42 //设置ConcreteStateA的下一状态是ConcreteStateB 43 context.State = new ConcreteStateB(); 44 } 45 } 46 /// <summary> 47 /// 具体的State类,实现一个与Context的一个状态相关的行为 48 /// </summary> 49 class ConcreteStateB : State 50 { 51 public override void Handle(Context context) 52 { 53 //设置ConcreteStateB的下一状态是ConcreteStateA 54 context.State = new ConcreteStateA(); 55 } 56 }
客户端调用:
1 public static void Main() 2 { 3 Context c = new Context(new ConcreteStateA());//设置Context的初始状态为ConcreteStateA 4 //不断的请求,同时更改状态 5 c.Request(); 6 c.Request(); 7 c.Request(); 8 c.Request(); 9 c.Request(); 10 Console.ReadKey(); 11 }
状态模式的好处就是将特定状态的行为局部化,并将不同状态的行为分割开来。
好,通过这个模式,我们来应用到我们的案例中来:
1 public abstract class State 2 { 3 public abstract void WrithProgram(Work w); 4 } 5 6 public class Work 7 { 8 private State current; 9 10 public Work() 11 { 12 current = new ForenoonState();//工作初始化为上午工作状态,即上午9点 13 } 14 private double hour; 15 16 public double Hour//"钟点"属性,状态转换的依据 17 { 18 get 19 { 20 return hour; 21 } 22 23 set 24 { 25 hour = value; 26 } 27 } 28 29 public bool Finish 30 { 31 get 32 { 33 return finish; 34 } 35 36 set 37 { 38 finish = value; 39 } 40 } 41 42 private bool finish = false; 43 44 public bool TaskFinished//"任务完成"属性,是否能下班的依据 45 { 46 get { return finish; } 47 set { finish = value; } 48 } 49 50 public void SetState(State s) 51 { 52 current = s; 53 } 54 55 public void WriteProgram() 56 { 57 current.WrithProgram(this); 58 } 59 } 60 //上午工作状态 61 public class ForenoonState : State 62 { 63 public override void WrithProgram(Work w) 64 { 65 if (w.Hour < 12) 66 { 67 Console.WriteLine($"当前时间:{w.Hour}点,上午工作,状态好"); 68 } 69 else//超过12点,则转入中午工作状态 70 { 71 w.SetState(new NoonState()); 72 w.WriteProgram(); 73 } 74 } 75 } 76 //中午工作状态 77 public class NoonState:State 78 { 79 public override void WrithProgram(Work w) 80 { 81 if (w.Hour < 13) 82 { 83 Console.WriteLine($"当前时间:{w.Hour}点,午饭 犯困 休息"); 84 } 85 else//超过13点,则转入下午工作状态 86 { 87 w.SetState(new AfternoonState()); 88 w.WriteProgram(); 89 } 90 } 91 } 92 //下午工作状态 93 public class AfternoonState:State 94 { 95 public override void WrithProgram(Work w) 96 { 97 if (w.Hour < 17) 98 { 99 Console.WriteLine($"当前时间:{w.Hour}点,下午工作,状态一般"); 100 } 101 else//超过17点,则转入旁晚工作状态 102 { 103 w.SetState(new EveningState()); 104 w.WriteProgram(); 105 } 106 } 107 } 108 //傍晚工作状态 109 public class EveningState:State 110 { 111 public override void WrithProgram(Work w) 112 { 113 if (w.TaskFinished)//完成任务则进入下班状态 114 { 115 w.SetState(new RestState()); 116 w.WriteProgram(); 117 } 118 else 119 { 120 Console.WriteLine($"当前时间:{w.Hour}点,加班!"); 121 } 122 } 123 } 124 //下班状态 125 public class RestState:State 126 { 127 public override void WrithProgram(Work w) 128 { 129 Console.WriteLine($"当前时间:{w.Hour}点,下班回家了"); 130 } 131 }
客户端调用:
1 public static void Main() 2 { 3 Work emergencyProject = new Work(); 4 emergencyProject.Hour = 9; 5 emergencyProject.WriteProgram(); 6 emergencyProject.Hour = 10; 7 emergencyProject.WriteProgram(); 8 emergencyProject.Hour = 12; 9 emergencyProject.WriteProgram(); 10 emergencyProject.Hour = 13; 11 emergencyProject.WriteProgram(); 12 emergencyProject.Hour = 14; 13 emergencyProject.WriteProgram(); 14 emergencyProject.Hour = 17; 15 emergencyProject.TaskFinished = false; 16 emergencyProject.WriteProgram(); 17 Console.ReadKey(); 18 }
这就是状态模式了。
今天状态模式就讲到这里,下一篇我们会讲 适配器模式
本系列将持续更新,喜欢的小伙伴可以点一下关注和推荐,谢谢大家的支持。