状态模式(一)
作者:互联网
本人从事仪器仪表行的软件开发工作,在软件的业务逻辑中,经常需要去对仪器的运行流程进行控制,一种方法就是开启一个while循环,通过循环不断地去查询状态的值,然后在循环内部根据状态值去执行特定的操作。示例代码如下:
static void Main(string[] args) { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); Run(StateType.A, cancellationTokenSource.Token);//阻塞了主线程,实际可能会开启线程或者异步去执行 }
private static void Run(StateType state, CancellationToken token)
{
while (token != null && !token.IsCancellationRequested)
{
switch (state)
{
case StateType.A:
HandleWorkA();
state = StateType.B;
break;
case StateType.B:
HandleWorkB();
state = StateType.C;
break;
case StateType.C:
HandleWorkC();
state = StateType.D;
break;
case StateType.D:
HandleWorkD();
state = StateType.E;
break;
case StateType.E:
HandleWorkE();
state = StateType.A;
break;
}
Thread.Sleep(1000);
}
}
private static void HandleWorkE()
{
Console.WriteLine("运行业务逻辑E");
}
private static void HandleWorkD()
{
Console.WriteLine("运行业务逻辑D");
}
private static void HandleWorkC()
{
Console.WriteLine("运行业务逻辑C");
}
private static void HandleWorkB()
{
Console.WriteLine("运行业务逻辑B");
}
private static void HandleWorkA()
{
Console.WriteLine("运行业务逻辑A");
}
}
enum StateType
{
A,
B,
C,
D,
E
}
以上代码开启了一个循环,在循环中,我们通过switch case 不断地去判断state的值,然后处理相应的业务,在业务处理结束时,我们会更新state的值,从而实现流程的跳转。
观察上面的代码我们可以发现几个特点:
1、状态切换的”动力源“是while循环。在上述代码中,HandleWorkA()、HandleWorkB()等等业务逻辑之所以能够被执行,状态之所以能够切换,其”动力“正是来自于while循环。
2、各个case下执行的代码逻辑是基本相似的。在每个case下,都会执行各自的业务逻辑,并且会决定下一个循环中switch的state值,在本例中就是去执行HandleWorkA()、HandleWorkB()等方法,在执行完这些方法后,状态state进行了更新。
3、新的状态,是由老的状态决定的。在每个case的break之前,其实已经知道下一次循环的状态了。比如在执行case A的业务后,我们就知道,下一个执行的必定是case B的业务。
由此我们想到,如果我们在执行case A的业务之后,直接调用执行case B的业务,那不就不需要while循环来给状态之间的跳转提供”动力“了吗?那是否在caseA 的HandleWorkA()之后调用HandleWorkB()就可以了呢?显然是不行的,因为如果这样做,在调用完HandleWorkB()我们同样还需要考虑运行HandleWorkC()的问题,最后代码就成了以下这样:
private static void Run(StateType state, CancellationToken token) { while (token != null && !token.IsCancellationRequested) { switch (state) { case StateType.A: HandleWorkA(); HandleWorkB(); HandleWorkC(); HandleWorkD(); HandleWorkE(); break; } } }
究其原因,是因为我们在执行完当前case的业务之后调用新的case的业务时,调用的是具体的方法,因此,我们在代码中必须调用不同的方法。
而上面我们提到”各个case下执行的代码逻辑是基本相似的“,因此我们考虑将每个case下的代码逻辑进行抽象,从而实现统一的调用。由此引出了本文的主题,状态模式,以上代码,使用状态模式的实现如下:
static void Main(string[] args) { StateMachine stateMachine = new StateMachine(new StateA()); stateMachine.UpdateState(); } interface IState { void Handle(StateMachine stateMachine);//把stateMachine传到Handle方法里,因为State业务逻辑可能需要用到stateMachine } class StateMachine { IState state; public StateMachine(IState initState) { this.state = initState; } public void UpdateState() { state.Handle(this); } } class StateA : IState
{
public void Handle(StateMachine stateMachine)
{
Console.WriteLine("运行业务逻辑A");
Thread.Sleep(1000);
new StateB().Handle(stateMachine);
}
}
class StateB : IState
{
public void Handle(StateMachine stateMachine)
{
Console.WriteLine("运行业务逻辑B");
Thread.Sleep(1000);
new StateC().Handle(stateMachine);
}
}
class StateC : IState
{
public void Handle(StateMachine stateMachine)
{
Console.WriteLine("运行业务逻辑C");
Thread.Sleep(1000);
new StateD().Handle(stateMachine);
}
}
class StateD : IState
{
public void Handle(StateMachine stateMachine)
{
Console.WriteLine("运行业务逻辑D");
Thread.Sleep(1000);
new StateE().Handle(stateMachine);
}
}
class StateE : IState
{
public void Handle(StateMachine stateMachine)
{
Console.WriteLine("运行业务逻辑E");
Thread.Sleep(1000);
new StateA().Handle(stateMachine);
}
}
对比两种方式我们写的代码,我们发现了一些改变:
1、switch case没有了,取而代之的是多个State,我们将不同状态要执行的业务逻辑封装到了StateA-StateE中;
2、while循环没有了,状态跳转的实现,是通过在每个状态的Handle方法结束前执行新状态的Handle方法实现的,如在StateA的Handle方法末尾,我们执行了new StateB().Handle(stateMachine);
那么在本例中使用状态模式的好处是什么呢?我概括为以下几个方面:
1、对不同状态的业务逻辑进行了封装,代码逻辑的封装性更好
2、去掉了while 和switch case,在新增状态时,只需要定义新的状态类就可以,代码的主调用过程( StateMachine stateMachine = new StateMachine(new StateA()); stateMachine.UpdateState();)不变,因而拓展性更好
3、swit case需要判断所有可能的状态,而在State类中,我们只会关注和State相关的状态,如在StateA中我们只关注StateA执行的业务逻辑DoWorkA,以及它的后续状态StateB。
本文中stateMachine.UpdateState();在运行了之后,再没有接受外部的控制,状态的跳转是通过状态的业务逻辑控制的,是自驱动的,那如何让外部业务逻辑来驱动状态跳转呢?这一问题将在下一篇文章中介绍。
标签:case,状态,Handle,StateType,stateMachine,void,模式,state 来源: https://www.cnblogs.com/fastexpress/p/16094609.html