热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入浅出设计模式(19)——State模式

“标准示范”就是刚刚上边列出来的if…elseif…else…语句,这样的语句大多被用来判断提交了什么动作,然后处理的。当然,需求是在不断变化中的,每当新加了一个业务的提交处理的时候,怎么办呢?绝大

“标准示范”就是刚刚上边列出来的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改变时,我们需要把它的改变打印出来。(当然,我们得假设各个打印的实现逻辑是彼此不同的)。

先看一下

常规的实现:

Java代码 State模式 - l_gx396696760 - L_gx

  1. public class ContextOriginal   
  2. {   
  3.      private String state;   
  4.     
  5.      public String getState()   
  6.      {   
  7.          return state;   
  8.      }   
  9.     
  10.      public void setState(String state)   
  11.      {   
  12.          this.state = state;   
  13.      }   
  14.     
  15.      public void print(String msg)   
  16.      {   
  17.          System.out.println(msg);   
  18.      }   
  19.     
  20.      public void printImplFirst()   
  21.      {   
  22.          this.print("printImplFirst: " + this.state);   
  23.      }   
  24.     
  25.      public void printImplSecond()   
  26.      {   
  27.          this.print("printImplSecond: " + this.state);   
  28.      }   
  29.     
  30.      public void printImplThird()   
  31.      {   
  32.          this.print("printImplFinal: " + this.state);   
  33.      }   
  34.     
  35.      public void execute()   
  36.      {   
  37.          if(this.state != null)   
  38.          {   
  39.              if(this.state.equals("first"))   
  40.              {   
  41.                  this.printImplFirst();   
  42.              }   
  43.              else if(this.state.equals("second"))   
  44.              {   
  45.                  this.printImplSecond();   
  46.              }   
  47.              else if(this.state.equals("third"))   
  48.              {   
  49.                  this.printImplThird();   
  50.              }   
  51.              else  
  52.              {   
  53.                  throw new IllegalArgumentException(   
  54.                          "illegalArgumentException: this.state = " + this.state);   
  55.              }   
  56.          }   
  57.          else  
  58.          {   
  59.              throw new NullPointerException("this.state is null");   
  60.          }   
  61.      }   
  62.  }   

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

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

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

我们在Context中加入成员StateHandler,

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

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

(见附件)

StateHandler抽象类的代码:

Java代码 State模式 - l_gx396696760 - L_gx

  1. package patterns.state;   
  2. import patterns.state.context.Context;   
  3.   
  4. public abstract class StateHandler   
  5. {   
  6.     public abstract void handle(Context context);    
  7. }   

StateHandler的几个实现代码:(只写出了一个,其它实现与之类似)

Java代码 State模式 - l_gx396696760 - L_gx

  1.   
  2. package patterns.state.impl;   
  3.   
  4. import patterns.state.StateHandler;   
  5. import patterns.state.context.Context;   
  6.   
  7. public class ConcreteStateHandlerFirst extends StateHandler   
  8. {   
  9.     public void handle(Context context)   
  10.     {   
  11.         if(context != null)   
  12.         {   
  13.             String state = context.getState();   
  14.             if(state != null && state.equals("first"))   
  15.             {   
  16.                 System.out   
  17.                         .println("ConcreteStateHandlerFirst: context.state = "  
  18.                                 + context.getState());   
  19.             }   
  20.         }   
  21.     }   
  22. }  

Context类的实现代码:

Java代码 State模式 - l_gx396696760 - L_gx

  1. package patterns.state.context;   
  2. import patterns.state.StateHandler;   
  3. import patterns.state.impl.ConcreteStateHandlerFirst;    
  4. public class Context   
  5. {   
  6.     private String state;   
  7.     private StateHandler stateHandler;    
  8.   
  9.     public Context()   
  10.     {       
  11.   
  12.     }    
  13.   
  14.     public Context(StateHandler stateHandler)   
  15.     {   
  16.         this.setStateHandler(stateHandler);   
  17.     }   
  18.   
  19.     public String getState()   
  20.     {   
  21.         return state;   
  22.     }    
  23.   
  24.     public void setState(String state)   
  25.     {   
  26.         this.state = state;   
  27.     }    
  28.   
  29.     public StateHandler getStateHandler()   
  30.     {   
  31.         return stateHandler;   
  32.     }    
  33.   
  34.     public void setStateHandler(StateHandler stateHandler)   
  35.     {   
  36.         this.stateHandler = stateHandler;      
  37.     }    
  38.   
  39.     public void execute()   
  40.     {   
  41.         if(this.stateHandler != null)   
  42.         {   
  43.             stateHandler.handle(this);   
  44.         }   
  45.         else  
  46.         {   
  47.             System.out.println("member varible state is null");   
  48.         }   
  49.     }   
  50. }   

好了,现在State模式的实现写完了。

现在我们什么时候想处理state为first的时候,只需要

Java代码 State模式 - l_gx396696760 - L_gx

  1. context.setStateHandler(new ConcreteStateHandlerFirst());   
  2. context.execute();  

context.setStateHandler(new ConcreteStateHandlerFirst());context.execute();

再添加一个状态也很简单,我们不需要修改Context类,只需要添加一个StateHandler类的实现,然后在context的状态改变之前,将处理的StateHandler类设定为新实现的类就OK了。

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

State模式的特点:

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

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

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

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

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

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

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

总结:

1.尽量不要使用太多分支的语句。

2.如果函数超过一屏,你就需要考虑是否需要把功能分割了。如果你写了个3000行的“超长”的函数,以后维护的人会很不爽,也会遭到bs的。

3.如果不幸遇到必须使用太多分支且分支与类的某一个状态有关且经常改变,那可以考虑使用state模式,防止“腐坏”。

4.牢记OO设计原则:“找出应用中需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起”。


推荐阅读
author-avatar
最最后的力气撑起最灿烂的微笑
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有