热门标签 | 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设计原则:“找出应用中需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起”。


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • springmvc学习笔记(十):控制器业务方法中通过注解实现封装Javabean接收表单提交的数据
    本文介绍了在springmvc学习笔记系列的第十篇中,控制器的业务方法中如何通过注解实现封装Javabean来接收表单提交的数据。同时还讨论了当有多个注册表单且字段完全相同时,如何将其交给同一个控制器处理。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
author-avatar
315热点关注
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有