`
iwindyforest
  • 浏览: 233664 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

学习设计模式之State

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

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

State模式介绍:
tate模式属于对象行为型模式,

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

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

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

State模式示例:

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

先看一下
常规的实现:
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…”,典型的使代码变质的方法。



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

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

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

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

下面是类图描述:(从类图上来看,state模式和strategy模式的类图是非常相似的)

(见附件)
State抽象类的代码:
package patterns.state;
import patterns.state.impl.Context;

public abstract class State {
    public abstract void handle(Context context);
    public abstract void changeState(Context context);
}
 
State的几个实现代码:(只写出了一个,其它实现与之类似)
 
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();
    }
}



Context类的实现代码:
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的状态改变多少次,添加了多少个状态,我们都不需要修改它的源代码,而只是通过添加新的实现方法就可以搞定了,这样达到了防止“代码腐坏”的目的。

State模式的特点:

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

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

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

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

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

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

使用状态模式将会导致设计中类的数目大量增加



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


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




总结:

1.尽量不要使用太多分支的语句。
2.如果函数超过一屏,你就需要考虑是否需要把功能分割了。如果你写了个3000行的“超长”的函数,以后维护的人会很不爽,也会遭到bs的。
3.如果不幸遇到必须使用太多分支且分支与类的某一个状态有关且经常改变,那可以考虑使用state模式,防止“腐坏”。
4.牢记OO设计原则:“找出应用中需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起”。
 
  • 大小: 8.7 KB
分享到:
评论
29 楼 longrm 2009-01-05  
最常见的就是dao,比如写一个basedao,然后oracledao、msdao。。都继承basedao,在弄个daofactory来生成dao,然后要新增dao的时候只需再加一个继承basedao的类就可以
28 楼 longrm 2009-01-05  
这个不就是用了抽象类和接口吗?state设计模式,貌似很多模式都用到了,寒。。。。
27 楼 iwindyforest 2009-01-05  
skeleton 写道
楼主明显写错了。
state模式的意图就是通过state的多态来消除判断状态的分支,以楼主这个例子,实现类应该是stateFirst,stateSecond,覆盖的父类方法是printImpl:stateFirst.printImpl(){ this.print("printImplFirst: " + this.toString())}等等。
在实现类里再有类似这样的语句  if(state != null && state.equals("first")) 实在是很搞笑的事情。
另外例子也不恰当,state模式一般应用的场景还应该包括各个状态之间的转换,比如业务的状态从stateFirst在excute之后将转换为stateSecond,否则直接用多态就可以了,没必要再维护一个context


状态的转换,就不说了,前面已经讨论过了.

你说的利用state的多态是状态模式消除分支的思想,
昨天看了下GoFBook总算明白了, 这个例子确实有问题....
应该不存在state--> StateHandler 而是State是个抽象类, 然后对应每一个State的子类, 有handle方法...
当然这是GoF给出的标准实现方法...但不是我想表达的思想,
我以后把这个例子重构下吧
26 楼 skeleton 2009-01-05  
楼主明显写错了。
state模式的意图就是通过state的多态来消除判断状态的分支,以楼主这个例子,实现类应该是stateFirst,stateSecond,覆盖的父类方法是printImpl:stateFirst.printImpl(){ this.print("printImplFirst: " + this.toString())}等等。
在实现类里再有类似这样的语句  if(state != null && state.equals("first")) 实在是很搞笑的事情。
另外例子也不恰当,state模式一般应用的场景还应该包括各个状态之间的转换,比如业务的状态从stateFirst在excute之后将转换为stateSecond,否则直接用多态就可以了,没必要再维护一个context
25 楼 zhangsatanyang 2009-01-04  
格外认真的看完之后,发现没什么用!
24 楼 chbest 2008-11-03  
Factroy.getHandler(context.getState());
里面还不是一样要写 if else?
23 楼 sea7 2008-11-03  
个人感觉从你的代码看起来你这应该是vistor pattern,而不是什么state pattern.
vistor pattern : 当新定义一个handler操作时,并不需要更改Context的结构。
而对于state pattern来说,当状态改变时,应该有state自己改变自己的行为而不是由一个handler来改变。
22 楼 WhisperXD 2008-10-25  
渐行渐远 写道
我不懂模式,所以每次做东西都是根据情况选取最合适的方式来设计。后来学了点模式,发现自己不知不觉用了模式,但分不清是哪一个模式,只知道那种情况下最合适。

我觉得懂模式的人往往做设计时总是先想着用什么模式来套用,而不是想着用什么样的设计最合适,陷入了模式的误区。

一点浅见,请大家批评指正


我在刚开始读很多模式书的时候也会这样,不过后来就变了,有本书改变了我的看法,就是<<refactoring to pattern.>>
读过以后我感觉现在更“深入”了些,也会在开始想到可能用到哪些模式,但开始却不去用了。。。。
21 楼 WhisperXD 2008-10-25  
taupo 写道
模式不是万能的
难道为了少写几个ifelse就要用state模式吗
我觉得state模式不过是把if -else语句换成了一个单独的类吧了

当然,维护起来是方便些,不过类大量增加也让人烦

我到有个解决办法。。。。。。。。。。

珍爱生命,远离JAVA  :)

大部分情况下不需要用到模式,我个人现在倾向于xp和重构那一套知识。
但很多时候用模式能够很好的解决问题。比如有30多个if else,里面有n多逻辑和接口调用。我不信一个正常的人能在短时间内明白里面的意思。

而state模式,本身的目的也不是为了替换if-else。。。如果把它理解为简单的替换。。方向就错了。。。
state的理念我认为是状态转换。也就是从某种状态转换为另外一种状态,或者有一套自己的转换过程,比如a->b->c状态,他们内部自己去管理这个状态具体是用哪一个流程来转换。类似一个复杂的开关控制面板系统。或者红绿灯。
20 楼 taupo 2008-10-25  
模式不是万能的
难道为了少写几个ifelse就要用state模式吗
我觉得state模式不过是把if -else语句换成了一个单独的类吧了

当然,维护起来是方便些,不过类大量增加也让人烦

我到有个解决办法。。。。。。。。。。

珍爱生命,远离JAVA  :)
19 楼 reg 2008-10-23  
maxima 写道
lz的方法需要往context写注入hander方法,那就需要改变原来实例属性、
一般不会这么做吧。
如果加入工厂模式来实现比较合理一点。
假设有个Factroy 提供根据state 返回hander类
那么
StateHandler   hander = Factroy.getHandler(context.getState());
hander.handle(context);


实际中,这种用法比较多~

运用state模式可能类更多了,代码也多了,但是程序的维护性和扩展性会更好,特别是在业务越来越复杂的情况下,优势更明显~
18 楼 maxima 2008-10-16  
lz的方法需要往context写注入hander方法,那就需要改变原来实例属性、
一般不会这么做吧。
如果加入工厂模式来实现比较合理一点。
假设有个Factroy 提供根据state 返回hander类
那么
StateHandler   hander = Factroy.getHandler(context.getState());
hander.handle(context);
17 楼 black_zerg 2008-10-15  
看了这个例子,主要的感觉是很麻烦。还是if else 省事。
这个没什么特别要求还是别用了,一堆代码还不好懂。另外那个修改过的版本是比较好的,比较实用的例子
16 楼 Ethan 2008-09-14  
可以打个比方,一个计算器上面有很多个按钮,没个按钮其实就对应一个state,这样每当user按下某个按钮的时候其实已经提交了需要执行的某种state了,然后计算器会根据这个state调用适当的handler来处理请求了。
15 楼 rainerWJY 2008-09-08  
linpyi 写道
不一定是为了使用设计模式而去改变代码,
分支细了有时候可能导致类爆炸

不过写的不错


以前讨论过。如果不在乎那点性能,可以考虑使用InnerClass来解决类爆炸的问题。同时也比较容易规范一些。
14 楼 xiaopang106 2008-09-08  
lz写得很不错啊,我感觉和《headfirst 设计模式》中的第一个例子有点相似,只是这个设计模式好像并不是为了用来解决 if{}else{}的。才刚刚看《headfirst 设计模式》,也不知道对不对 呵呵
13 楼 linpyi 2008-09-08  
不一定是为了使用设计模式而去改变代码,
分支细了有时候可能导致类爆炸

不过写的不错
12 楼 czlonly 2008-09-08  
分支分化成类,类就太多了
11 楼 luckaway 2008-09-06  
建议你去看一下《重构》。
当代码里有多处if else语句的时候。
1:找到动态嵌入点。用state模式或Strategy模式,state通常情况下是无法用的,一般都是用Strategy模式。
2:如果没有动态切入点。如你的ContextOriginal。那就要把if else语句抽取出来放到的一个方法里,而且这个方法除if else没有其他的代码。然后在不同的地方通用它。
3:下面是我重构的代码。我这样修改了对现在没有多大意义,因为你excute()里没有其他业务代码。通常情况是还有其他业务代码的。但是可扩展性增强了

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() {
		printImpl(this.state);
	}

	public void printImpl(String state) {
		if (state == null) {
			throw new NullPointerException("this.state is null");
		} else 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);
		}
	}
}


在coding的时候。不要刻意想着一定要吧某种设计模式给用上。
“设计模式知识OO设计上一部分内容”这句话可能不完全准确。能理解就好
00设计原则
http://samuelray.iteye.com/blog/170463
10 楼 a_nuo 2008-09-06  
写的很好,现在在学设计模式,很有帮助

相关推荐

    设计模式C++学习之状态模式(State)

    状态模式是一种行为设计模式,它允许对象在内部状态改变时改变其行为,对象看起来似乎修改了它的类。这种模式常用于处理对象的状态变化,并且使代码结构清晰,易于维护。 在C++中,状态模式通常包含以下几个关键...

    24种设计模式以及混合设计模式

    通过学习和应用这些设计模式,开发者不仅可以提高代码的可读性、可维护性和可扩展性,还能提升团队间的沟通效率,因为设计模式是软件工程中的通用语言。对于任何有志于提升软件开发水平的人来说,理解和掌握设计模式...

    java设计模式中英文各种版本打包下载 学习设计模式必备材料

    学习Java设计模式,不仅可以提高个人编程技能,还能提升团队协作效率,使得项目更易于理解和维护。在J2EE架构中,设计模式的应用尤为重要,因为J2EE通常涉及多层架构,包括表现层、业务逻辑层和数据访问层,每个层都...

    设计模式那点事

    《设计模式那点事》这本书的PPT为我们提供了一种深入理解和学习设计模式的途径。在这里,我们将深入探讨设计模式的核心概念、分类以及它们在实际开发中的应用。 首先,设计模式基于面向对象编程思想,其核心目标是...

    23种面向对象设计模式

    文档中的“23种设计模式学习笔记.doc”可能包含了对这23种模式的详细解释和实例,而“设计模式之我爱我家.doc”可能从一个更生活化的角度来阐述设计模式的概念。“软件23种设计模式,超级经典的.pdf”可能是对这些...

    设计模式学习笔记总结

    这里我们聚焦于C#语言中的设计模式学习笔记,涵盖了多种经典的设计模式,如合成模式、桥梁模式、装饰模式、享元模式、门面模式、命令模式、工厂方法、策略模式、代理模式以及状态模式。下面将对这些模式逐一进行详细...

    《设计模式》实战---状态模式(State Pattern)

    状态模式是一种行为设计模式,它使你能在运行时改变对象的行为。在状态模式中,一个对象的状态变化会导致其行为的变化。...通过学习这篇文章,开发者能够更好地掌握这一设计模式,提高代码的可读性和可维护性。

    设计模式可复用面向对象软件的基础(PDF,学习设计模式必备)

    这本书由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位作者编写,是设计模式领域的权威之作。 书中介绍了23种设计模式,这些模式可以分为三类:创建型模式(Creational Patterns)、结构型模式...

    设计模式精解-GoF 23种设计模式解析附C++实现源码

    学习设计模式并非易事。初学者可能会觉得抽象、难以理解。但随着实践的深入,逐渐能够领悟其背后的原理与价值。正如文中所述,“自己学会一门知识(技术)、表达出来、教会别人、记录下来”,这是学习任何复杂概念的...

    《设计模式:可复用面向对象软件的基础》学习并理解 23 种设计模式

    学习设计模式对于软件开发人员来说至关重要,主要基于以下几点理由: - **经验借鉴**:设计模式是基于众多专家的经验和智慧,提供了经过实践检验的解决方案。 - **提高可维护性和可复用性**:通过遵循已知的最佳...

    设计模式精解-GoF 23种设计模式解析附C++.pdf

    设计模式的学习过程通常分为四个阶段:学习、表达、教授、记录。每个阶段都需要不同的技能和深度的理解。 #### 0.2 设计模式解析后记 在完成所有设计模式的学习和解析之后,开发者会发现自己已经进入了一个新的...

    二十三种设计模式【PDF版】

    设计模式之 State(状态) 状态是编程中经常碰到的实例,将状态对象化,设立状态变换器,便可在状态中轻松切换. 设计模式之 Memento(注释状态?) 很简单一个模式,就是在内存中保留原来数据的拷贝. 设计模式之 ...

    《Java设计模式》课后答案-刘伟.rar

    行为型设计模式如策略(Strategy)、模板方法(Template Method)、观察者(Observer)、命令(Command)、迭代器(Iterator)、访问者(Visitor)、状态(State)、职责链(Chain of Responsibility)、解释器...

    C++的设计模式学习资料

    正如文中提到的:“设计模式之于面向对象系统的设计和开发的作用就有如数据结构之于面向过程开发的作用一般”,学习和应用设计模式是一项长期的过程,需要不断地实践和反思。希望通过对这些设计模式的深入研究和实践...

    JAVA设计模式-chm版

    三、学习设计模式的方法: 1. 阅读经典书籍,如《设计模式:可复用面向对象软件的基础》(GOF设计模式)。 2. 实践编程,将所学应用到实际项目中。 3. 分析和重构现有代码,找出并应用设计模式。 4. 参与讨论和分享...

    c#设计模式

    通过学习和实践这些设计模式,C#开发者可以更好地理解和解决复杂的软件设计问题,提高代码质量,同时也为团队协作提供了通用的语言。在实际项目中,可以根据需求灵活选用适合的设计模式,从而实现高效、可扩展和易于...

    研磨设计模式(完整带书签).part2.pdf

    也可以作为高效学生深入学习设计模式的参考读物! 第1章 设计模式基础 第2章 简单工厂 第3章 外观模式 第4章 适配器模式(Adapter) 第5章 单例模式(Singleton) 第6章 工厂方法模式(Factory Method) 第7章...

    设计模式之禅第2版超清

    通过学习和实践设计模式,开发者可以更好地理解并利用这些框架,提高代码的可读性和可维护性。 总之,《设计模式之禅》第二版可能会深入剖析Java开发中的设计模式,帮助读者掌握这些模式的精髓,提升软件设计能力。...

    《java设计模式》课后习题模拟试题解答——刘伟.zip

    本资料“《java设计模式》课后习题模拟试题解答——刘伟.zip”主要涵盖了Java设计模式的学习与应用,特别是针对刘伟教授的相关课程的课后习题及模拟试题的解答。 设计模式分为三大类:创建型、结构型和行为型模式。...

    设计模式23种类图

    设计模式是软件工程中的一种最佳实践,用于解决在软件开发过程中常见的问题。这些模式是经验丰富的开发者们在解决相似...学习和掌握这些设计模式,对于提升软件开发的专业技能,以及团队间的沟通效率都有着显著的帮助。

Global site tag (gtag.js) - Google Analytics