上文已经提到如何消除条件,通过多态,这是面向对象的显著特征之一。
在使用多态之前我们先把思路理理清楚。在这个程序中主要涉及了两个状态,以及在两个状态下发生的一些事件。我们用一个活动图把它形象的表示一下。
两个状态Get First Point和Get Second Point,在第一个状态移动鼠标没有任何反应,不要做什么事也不会发生状态变迁。如果鼠标点击一下也就是GetPoint事件发生,那么我们的状态将发生变化,转至Get Second Point状态。图中的@RecondStartPt是指在GetPoint事件发生时所要做的事,即记录下第一个点,也就是线段的起点。在第二个状态下移动鼠标也不会发生状态变迁,但是需要处理DragLine,也就是实现直线的脱拽效果。此时如果点击鼠标,那会引起状态转移,转移到Get First Point,并记下第二个点也就是线段的终点。
我们发现在每个状态下,只有两个事件GetPoint和MovePoint (RecordStartPt等等发生在他们之中),而每个状态的处理方法不同,从中可以明显看出多态的影子。
一个接口IUIState,包含两个方法GetPoint和MovePoint ,两个状态分别对应于两个具体实现类。
这就是一个典型的State模式的UML图(有关State模式,由于吕震宇的文章还没出来,就参考一下我的一片随笔吧),
MainForm中的MovePoint和GetPoint,它自己什么也不作只是简单的委托给State来完成,(此处的delegate和C#的那个delegate不是一个东西喔),MainForm只需要提供必要的UI操作的方法和状态变迁的方法供State使用。State在GetPoint和MovePoint事件处理中主要干两件事,一个做前面活动图中有@前缀的事,还有就是进行必要的状态转化。
这样我们的画图(其实就是画线)程序就完成了。采用了State模式完全解决了我在开头提出的3个缺点。而且还利于扩展,要有什么新的动作,也很方便实现。
但是目前的设计已经完美吗?没有。两个具体的State类竟然依赖于MainForm这个具体类,当然不合理了,根本就没法重用State这块了(蓝色区)。
所以我们要Design to interface,改成下面这个样子。
这样State这部分就不再依赖于具体了,以后让其他的Form实现ISateCallBack就可以使用State模式了,而且State模式部分根本不要改任何代码。(比如你不想在一个WindowsForm上画线,而是在自己的Canvas上作图。)
现在的State模式已经显的比较完美了,不过看到下面这段我也很不爽。
{
if (currentUIState != null)
{ currentUIState.GetPoint(i);
}
}
防御型语句我也不想看到。发挥一下State模式的威力吧,引入一个Null Object。
Null Object顾名思义什么也不作,让那两个方法为空就行了。
这样我们就去掉了那个if,一切变的那么简单。
在这个框架的基础上要实现添加新的行为,你会发现条理很清晰(跟最初的比比),不如你把它完善吧。
(注:本文只是演示重构和模式的实例,在逻辑上不是那么的完备,如果你有兴趣可以把它完善.比如工作生活提出的bug.)
参考资料―――Agile software development