学习设计模式之State

[size=x-large][b]发现问题:[/b][/size]
大家在Coding的时候,有没有用到很多的选择语句?像这样:

if(…)
{

}
else if(…)
{

}
else if(…)
{

}
else
{

}


我是经常碰到,最经常见到的地方就是在struts的业务逻辑――Action的实现的类里, 以前的程序员很牛B,Coding从来都已“just can run ”为最终标准。

下面这个函数:

public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws java.lang.Exception

的实现,一般都是一个函数里边写2000-3000行代码,从来不关心结构的问题。

“标准示范”就是刚刚上边列出来的if…else if…else…语句,这样的语句大多被用来判断提交了什么动作,然后处理的。当然,需求是在不断变化中的,每当新加了一个业务的提交处理的时候,[b]怎么办呢?[/b]绝大多数人的选择都是在最后一个else if的后面,else的前面,添加一个else if…,^_^,如此处理。长此以往,一段程序或许本来就有n个else if,然后维护又加了n个,本来2000行的函数,搞成了三千行。[b]哈哈,一个函数体三千行!何其壮观也!!![/b]
因此,我觉得,程序员在写判断语句的时候,用了多个else if就应该小心一点了,这个语句像某位大师所说的,是[b]在代码中加入了“坏味道”,会引起程序变质,成为“腐坏代码”的。 [/b]

[size=x-large][b]提出问题:[/b][/size]
那么,在出现要使用多个判断,并且判断逻辑经常改变的时候的时候,我们如何才能防止代码腐坏呢?
――[b]使用state模式就是一个比较好的方法[/b]

[b]State模式介绍:[/b]
tate模式属于对象行为型模式,

意图:允许一个对象在其内部状态改变时改变它的行为

适用场景:
1.一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2.一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。

以上是出自GoF的DesignPatterns的官方解释,但是归结到平常使用中,我认为最多的适用场景还是[b]替代if…if else…else多分支,实现更灵活的设计,防止代码腐坏。[/b]
[b]可以看出:上面提到的“经典示例”就是个典型的第2个场景[/b]

[size=x-large][b]State模式示例:[/b][/size]

一开始我们有这么个Context类,这个类有个state成员,当state改变时,我们需要把它的改变打印出来。(当然,我们得假设各个打印的实现逻辑是彼此不同的)。

先看一下
[b]常规的实现:[/b]

public class ContextOriginal
{
private String state;

public String getState()
{
return state;
}

public void setState(String state)
{
this.state = state;
}

public void print(String msg)
{
System.out.println(msg);
}

public void printImplFirst()
{
this.print("printImplFirst: " + this.state);
}

public void printImplSecond()
{
this.print("printImplSecond: " + this.state);
}

public void printImplThird()
{
this.print("printImplFinal: " + this.state);
}

public void execute()
{
if(this.state != null)
{
if(this.state.equals("first"))
{
this.printImplFirst();
}
else if(this.state.equals("second"))
{
this.printImplSecond();
}
else if(this.state.equals("third"))
{
this.printImplThird();
}
else
{
throw new IllegalArgumentException(
"illegalArgumentException: this.state = " + this.state);
}
}
else
{
throw new NullPointerException("this.state is null");
}
}
}


这样的实现方法的弊端刚开始已经分析的很清楚了,如果我们需要再加一个状态“ fourth”,则需要再加一个实现“printImplFourth()”,然后在“最后一个else if的后面,else的前面,添加一个else if…”,[b]典型的使代码变质的方法。[/b]



那么我们使用State模式来实现吧:

我们首先声明一个抽象类State,以后所有的实现都继承这个类来实现它的handle方法。

我们在Context中加入成员State,表示Context的当前状态,

然后把所有对Context的state的处理都通过继承State抽象类来实现。

下面是类图描述:(从类图上来看,state模式和strategy模式的类图是非常相似的)
[img]http://chenyinghua.iteye.com/upload/attachment/66340/c2348da6-0fff-3f22-a19c-b2bd53b03d9f.png[/img]
(见附件)
[b]State抽象类的代码:[/b]

package patterns.state;
import patterns.state.impl.Context;

public abstract class State {
public abstract void handle(Context context);
public abstract void changeState(Context context);
}

[b]State的几个实现代码:(只写出了一个,其它实现与之类似) [/b]
 
package patterns.state.impl;

import patterns.state.State;

public class FirstState extends State {

@Override
public void handle(Context context) {
System.out.println("Current State is : "
+ context.getSteate().toString());
this.changeState(context);
}

@Override
public void changeState(Context context) {
context.setState(new SecondState());

}

public String toString() {
return this.getClass().getSimpleName();
}

}



package patterns.state.impl;

import patterns.state.State;

public class SecondState extends State {

@Override
public void handle(Context context) {
System.out.println("Current State is : "
+ context.getSteate().toString());
this.changeState(context);
}

@Override
public void changeState(Context context) {
context.setState(new FirstState());
}

public String toString() {
return this.getClass().getSimpleName();
}
}



[b]Context类的实现代码: [/b]

package patterns.state.impl;

import patterns.state.State;

public class Context {
// context的当前状态
private State state;

public Context() {
this.state = new FirstState();
}

public State getSteate() {
return state;
}

public void setState(State state) {
this.state = state;
}

public void execute() {
this.state.handle(this);
}
}


好了,现在State模式的实现写完了。
现在我们什么时候想处理state为first的时候,只需要


package patterns.state.test;

import patterns.state.impl.Context;
import patterns.state.impl.FirstState;

public class Test {

public Test() {
Context context = new Context();
context.execute();
context.setState(new FirstState());
context.execute();
context.execute();
context.execute();
context.execute();
}

public static void main(String[] args) {
new Test();
}

}



通过使用了State模式,不管Context的状态改变多少次,添加了多少个状态,我们都不需要修改它的源代码,而只是通过添加新的实现方法就可以搞定了,这样达到了防止“[b]代码腐[/b]坏”的目的。

State模式的特点:

[b]State模式允许一个对象基于内部状态而拥有不同的行为

State模式允许使用类代表状态

Context会将行为委托给当前对象

通过将每个状态封装进一个类,我们把以后需要做的任何更改局部化了

状态(state)的转换可以由StateHandler类或者context类来控制

状态处理类(StateHandler)可以被多个Context实例共享。

[color=red]使用状态模式将会导致设计中类的数目大量增加[/color]
[/b]

[b]
[size=medium]需要注意的地方[/size]:
1. state之间的转换. State的转换可以在State类内部完成, 但是这样会带来的问题是各个State之间出现了关联, 那么如果一个State本身改变(减少, 增加) 会带来很大影响, 你就有可能看看当前发生改变的state是否也影响到了其它的state的执行. 不过经过跟同事的讨论发现也是在所难免的. 另外一个, state的转换也可以由其它类调用Context的setState()来完成, 感觉这种情况更不可靠, 因为这样的话其他类就必须知道context的特定state, 带来了不必要的耦合, 因此[color=red]建议对setState()的调用尽量放在Context和State这两处.[/color]


2. State的创建和销毁. 两个方法: 1.在context内部创建所有state, 一直维护从不销毁.2.在状态转换的时候才创建, 然后上一个state由JVM自动回收. 主要原则需要看实际情况是Context的State的变化是否频繁, 如果不频繁, 建议用第二种,比较优雅.

[/b]


总结:

1.尽量不要使用太多分支的语句。
2.如果函数超过一屏,你就需要考虑是否需要把功能分割了。如果你写了个3000行的“超长”的函数,以后维护的人会很不爽,也会遭到bs的。
3.如果不幸遇到必须使用太多分支且分支与类的某一个状态有关且经常改变,那可以考虑使用state模式,防止“腐坏”。
4.牢记OO设计原则:“找出应用中需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起”。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值