`
iwindyforest
  • 浏览: 235113 次
  • 性别: 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
分享到:
评论
9 楼 iwindyforest 2008-09-01  
to luckaway:
引用

状态应该是在运行StateHandler时候自动改变的。


你说的很正确,Context的状态State应该有很多个,
然后,改变这个Context的当前状态,一般是StateHandler在实现类的内部去改变....

外部只管去调用context.execute();就可以了....

但是Context最初的状态State也许会来自于两个地方:
1.初始化 2.初始化后,外部主动设置状态

而我这个例子因为是演示比较短,只照顾到了第二种情况。
8 楼 tywo45 2008-08-13  
此模式配合反射,代码量将大减
7 楼 ladofwind 2008-08-03  
感觉state模式有点类似开关,开始是设置2个开关,来代表"开","关",发现浪费材料,可以用一个,上去代表"开",下来代表"关",于是扩展开来,多功能的都可以用一个实现,按一次是"冷风",再按"换气",再按"除湿"......一堆功能封装起来了...呵呵,不知道理解的对不对
6 楼 zhiblin 2008-08-02  
功力不够看不懂 , 不过还是坚持看完了。相信以后能看懂,加油!!!!
5 楼 luckaway 2008-08-02  
状态应该是在运行StateHandler时候自动改变的。
我觉你的你这样做,是无法实现的
#         Context c = new Context(); 
#         c.setState("first"); 
#         c.setStateHandler(new ConcreteStateHandlerFirst()); 
#         c.execute();//state 改变成 “second” 
#         //c.setState("second"); 
#         c.setStateHandler(new ConcreteStateHandlerSecond()); 
#         c.execute();  //state又改变成“first”。没意义
state模式是要引入多态机制。操作不变,但是接受的类是在变的。






4 楼 iwindyforest 2008-07-10  
引用

請問..Conext設置狀態以及根據狀態設置對應該狀態的行為子類不也要用if else語句麼?
還是脫不開啊.


使用state模式的意图不是在于让if...else...语句消失,
而是取决于下面两个场景:

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

当你在解决问题时,碰到了这样的场景的时候,才有必要考虑使用state模式。


另外,应该在OOD的合适时机,合适的场景下使用“设计模式”,
如果仅仅为了使用某个模式,才去设计,就本末倒置了。
所以学习设计模式的时候,明确这个设计模式的使用的“意图”,也是很重要。
3 楼 jkluooop 2008-07-10  
請問..Conext設置狀態以及根據狀態設置對應該狀態的行為子類不也要用if else語句麼?
還是脫不開啊.
2 楼 iwindyforest 2008-07-09  
引用

不过运行后代码有点问题:state != null && state.equals("first")运行结果始终为false,代码中没有找到改变state的地方


这样的,Context类在初始化完了之后,是需要被其它对象调用,然后设置它的state属性,然后才去执行该state下的操作的,类似下面这样:
package patterns.state.test;
import patterns.state.context.Context;
import patterns.state.impl.ConcreteStateHandlerFirst;
import patterns.state.impl.ConcreteStateHandlerSecond;

public class Test
{
    public Test()
    {
        Context c = new Context();
        c.setState("first");
        c.setStateHandler(new ConcreteStateHandlerFirst());
        c.execute();
        c.setState("second");
        c.setStateHandler(new ConcreteStateHandlerSecond());
        c.execute();
    }

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

}
1 楼 kksss118 2008-07-09  
写的满好
不过运行后代码有点问题:state != null && state.equals("first")运行结果始终为false,代码中没有找到改变state的地方

相关推荐

    设计模式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)、结构型模式...

    《设计模式:可复用面向对象软件的基础》学习并理解 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章...

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

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

    设计模式之禅第2版超清

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

    设计模式23种类图

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

Global site tag (gtag.js) - Google Analytics