State模式定义:不同的状态,不同的行为;或者说每一个状态都有不同的行为。
State模式使用场合:State模式在实际使用中用的比较多,主要用于“状态的切换”。如果针对状态的if-else切换反复出现,应该考虑使用State模式。不只是根据状态,也有根据属性。如果对象属性不同,行为也就不同了。
下面,给出一个小的例题来说明问题:
十字路口红绿灯问题。根据灯的颜色输出相关的提示信息。代码如下:
现在看一下问题所在:关于对象状态的判断逻辑散落在整个类中。在这里,getStateInfo()和nextState()方法都对类状态进行判断后才给出相应的处理方式。对于红绿灯这类状态比较少的问题处理采用这种方式尚可,如果状态比较多的话问题就比较严重了。这里问题的本质是:不同的状态有不同的行为。从而引出今天的重构:【用State替换状态改变条件语句】。
下面就来简单的介绍一下【用State替换状态改变条件语句】重构。
适用范围:控制一个对象状态转换的条件表达式过于复杂。
解决方法:用处理特殊状态和状态转换的State类替换条件语句。
动机:重构实现State模式的主要原因:是为了对付过度复杂的状态改变条件逻辑,这种逻辑往往会散步在整个类中,控制对象的状态以及状态之间的转换。
在State模式中,需要创建代表对象特殊状态和状态间转换的类。包含状态改变的对象称为上下文。上下文把状态相关的行为委托到状态对象上。通过上下文引用不同的状态对象,状态对象可以在运行时实现状态转换。
把状态改变条件逻辑从类中去除,并搬移到表示不同状态的一系列类中,可以产生更简单的设计,它提供了观察状态转换的更好的鸟瞰图。另一方面,如果类中的状态转换逻辑很容易理解,就不需要重构到State模式了。
现在,看一下本重构的优缺点:
优点:
- 减少或去除状态改变条件逻辑
- 简化了复杂的状态改变逻辑
- 提供了观察状态改变的很好的鸟瞰图
缺点:
- 当状态转换逻辑已经易于理解的时候,会增加设计的复杂度
接下来,就将上述的红绿灯问题重构到State模式。
1.首先,使用【用类替换类型码】重构。将Cross类中的类型码提取称为一个类。关于【用类替换类型码】重构,在这里不多复述,见另一篇blog:http://blog.csdn.net/prince2270/archive/2009/09/10/4540677.aspx。在State模式中,上下文类被描述为"State:Context",状态超类被描述为"State:State"。重构后的结果如下:
Cross类:
State类(从类型码中提取出来的类):
2.状态超类State中的每个常量都引用着状态超类的一个实例。应用【提取子类】重构,为每个常量产生一个子类(描述为State:ConcreateState),然后更新超类中的常量,使它们引用相应的子类实例。最后,把状态超类设置为抽象类。
State类:
State子类:
3.在上下文类中找出根据状态转换逻辑来修改原始状态字段的方法。把这个方法复制到状态超类中,在修改最少的前提下是这个新方法可以运行。(一个常用的、简单的修改是把上下文类传入这个方法,以便新方法中的代码调用上下文类的方法)。最后,把上下文类中方法的方法体替换为对新方法的委托。
对上下文类中的每个根据状态转换逻辑来修改原始状态子uand方法重复这一步骤。
State类:
Cross类:
4.选择上下文类能够进入的状态,然后识别出状态超类中的哪些方法会是这个状态转换到其他状态。把这些识别出来的方法复制到与被选中的状态相关联的子类中,并去除无关的逻辑。最后删除状态超类中的方法实现。
State类:
State子类:
最后,程序得到了这样的结构,去除了程序中的if-else判断语句,是程序中状态改变的流程更加清晰。但也有一些副作用:原来在一个类中可以实现的功能,现在却需要五个类来完成。增加了程序结构的复杂性。