Java如何利用状态模式(state pattern)替代if else

大多数开发人员现在还在使用if else的过程结构,曾看过jdon的banq大哥写的一篇文章,利用command,aop模式替代if else过程结构。当时还不太明白,这几天看了《重构》第一章的影片租赁案例,感触颇深。下面我来谈一谈为什么要用state pattern替代if else,替代if else有什么好处,以及给出详细代码怎么替代if else。本文参考jdon的“你还在使用if else吗?”及《重构》第一章。

首先我们模仿影片租赁过程,顾客租凭影片,影片分为儿童片、普通片、新片。根据影片类型及租凭天数价格各不相同(优惠程度不同),用户累计积分不同。

OK ,现在我们使用 if else 表示。

 package com.qujingbo.movie;

 /**
 * <p/> Title:影片基类
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:47:55
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
 public class Movie {

  // 普通片标识
  public static int REGULAR = 1 ;

  // 新片标识
  public static int NEW_RELEASE = 2 ;

  // 儿童片标识
  public static int CHILDREN = 3 ;

  /**
  * 获取租赁影片总价
  *
  * @param movieCode
  * 影片类型
  * @param days
  * 租凭天数
  * @return 租赁影片总价
  * @throws MovieException
  * 没有影片类型抛出异常
  */
  public double getCharge( int movieCode, int days) throws MovieException {
  double result = 0 ;
  // 普通片
  if (movieCode == Movie.REGULAR)
  // 单价为2
  {
  result = 2 ;
  // 如果租赁天数大于2则,则优惠
  if (days > 2 ) {
  result += (days - 2 ) * 1.5 ;
  }
  // 返回总价
  return result;
  }
  // 最新发布片
  else if (movieCode == Movie.NEW_RELEASE) {
  // 新片没有优惠,单价为3
  return days * 3 ;
  }
  // 儿童片
  else if (movieCode == Movie.CHILDREN) {
  // 影片单价
  result = 1.5 ;
  // 如果租赁时间大于3天则做价格优惠
  if (days > 3 ) {
  result += (days - 3 ) * 1.5 ;
  }
  // 返回租赁影片总价
  return result;
  } else
  throw new MovieException( " 影片不存在 " );
  }

  /**
  * 获取租赁影片积分
  *
  * @param movieCode
  * 影片类型
  * @param days
  * 租凭天数
  * @return 租赁影片积分
  * @throws MovieException
  * 没有影片类型抛出异常
  */
  public double getIntegral( int movieCode, int days) throws MovieException
  {
  // 普通片
  if (movieCode == Movie.REGULAR)
  return days * 2 ;
  // 最新发布片
  else if (movieCode == Movie.NEW_RELEASE)
  return days * 3 ;
  // 儿童片
  else if (movieCode == Movie.CHILDREN)
  return days * 1.5 ;
    else
     throw new MovieException( " 影片不存在 " );

  }
}

OK ,我们看一下,现在的 Movie 完全符合租赁需求,通过 getIntegral(int movieCode,int days) 和 getCharge(int movieCode,int days) 来获得租赁积分及租赁价格。从开闭原则角度来看,如果要添加新的影片类型,我们必须修改 getIntegral(int movieCode,int days) 和 getCharge(int movieCode,int days) 这两个方法。而若要改变租赁价格、积分的优惠规则时,仍需要修改 getIntegral(int movieCode,int days) 和 getCharge(int movieCode,int days) 方法。现在看来,只有三种影片类型,维护还较方便。而当影片类型较多时,例如 10 种, 100 种影片类型,这样就是不可以想像的维护。

现在我们来看一下,使用 state pattern 来代替 if else 。先来个类图。

首先我们建立一个 abstract class Price 做为影片类型的基类,基类中含有两个 abstract 方法,获取总价格 getCharge(int days), 获取总积分 getIntegral(int days) 方法 , 继承 abstract classPrice 的三个影片类型儿童片 class ChilerenPrice, 普通片 class RegularPrice, 最新片 class NewReleasePrice 。分别实现 getCharge(int days),getIntegral(int days) 方法,实现方法写入计算价格的优惠方案及积分的方案。当需要修改方案时,我们只需在某个影片类的方法中对应修改就可以。若新增一个影片分类时,我们只需新增一个实现类实现 abstract class Price 类就 OK 。

class Movie 代表影片,其关联一个 Price 类,而 setPrice(String movieClass) 方法类似于一个工厂类,传入 movieClass 为包名类名,用 java 反射机制实例化一个具体传入 movieClass 的影片类型实现类,这样我们通过这几行代码就可以获得该影片类型的价格和积分。

 Movie regularMovie = new Movie();
regularMovie.setPrice(Movie.REGULAR);
System.out.println( " 普通影片租赁10天的价格 " + regularMovie.getPrice().getCharge( 10 ));
System.out.println( " 普通影片租赁10天的积分 " + regularMovie.getPrice().getIntegral( 10 ));

下面我们给出详细代码

abstract class Price价格基类

package com.qujingbo.movie;

/**
 * <p/> Title:
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:48:22
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
public abstract class Price {

 /**
  * 获取租赁影片价格需实现该此方法
  *
  * @param days
  *   租赁天数
  * @return 返回影片价格
  */
 public abstract double getCharge(int days);

 /**
  * 获取租赁影片积分需实现此方法
  *
  * @param days
  *   租赁天数
  * @return 返回影片积分
  */
 public abstract double getIntegral(int days);

}

儿童片ChildrenPrice类,实现abstract class Price ,实现儿童片租赁总价getCharge(int days)及儿童片租赁积分getIntegral(int days)。

package com.qujingbo.movie;

/**
 * <p/> Title:儿童片租赁积分、价格实现
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:49:04
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
public class ChildrenPrice extends Price {

 /**
  * 儿童片返回租赁积分,儿童片积分规则为: 根据
  */
 public double getIntegral(int days) {
  // 返回租赁影片积分
  return days * 1.5;
 }

 /**
  * 儿童片返回租赁价格
  */
 public double getCharge(int days) {
  // 影片单价
  double result = 1.5;
  // 如果租赁时间大于3天则做价格优惠
  if (days > 3) {
   result += (days - 3) * 1.5;
  }
  // 返回租赁影片总价
  return result;
 }

}

普通片RegularlPrice类,实现abstract class Price ,实现普通片租赁总价getCharge(int days)及普通片租赁积分getIntegral(int days)。

package com.qujingbo.movie;

/**
 * <p/> Title:普通片租赁积分、价格实现
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:50:10
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
public class RegularlPrice extends Price {
 /**
  * 普通片返回租赁积分,普通片积分规则
  */
 public double getIntegral(int days) {
  // 返回租赁影片积分
  return days * 2;
 }

 /**
  * 普通片返回租赁价格
  */
 public double getCharge(int days) {
  // 单价为2
  double result = 2;
  // 如果租赁天数大于2则,则优惠
  if (days > 2) {
   result += (days - 2) * 1.5;
  }
  // 返回总价
  return result;
 }

}

最新发布片NewReleasePrice类,实现abstract class Price ,实现最新发布片租赁总价getCharge(int days)及最新发布片租赁积分getIntegral(int days)。

package com.qujingbo.movie;

/**
 * <p/> Title:最新发布片租赁积分、价格实现
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:48:51
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
public class NewReleasePrice extends Price {
 /**
  * 最新发布片返回租赁积分,最新发布片积分规则
  */
 public double getIntegral(int days) {
  // 返回租赁影片积分
  return days * 3;
 }

 /**
  * 最新发布片返回租赁价格
  */
 public double getCharge(int days) {
  // 新片没有优惠,单价为3
  return days * 3;
 }

}

电影Movie类,setPrice(String movieClass)(工厂)方法,通过java反射机制实现movieClass(包名,类名)类。若没有movieClass这个类,则抛出MovieException异常。

package com.qujingbo.movie;

/**
 * <p/> Title:影片类
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:47:55
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
public class Movie {
 // 普通片标识
 public static String REGULAR = "com.qujingbo.movie.RegularlPrice";

 // 新片标识
 public static String NEW_RELEASE = "com.qujingbo.movie.NewReleasePrice";

 // 儿童片标识
 public static String CHILDREN = "com.qujingbo.movie.ChildrenPrice";

 private Price price;

 public Price getPrice() {
  return price;
 }

 /**
  * 确定返回具体某个影片类型的实现类,有点像工厂
  *
  * @param movieCode
  *   影片类型
  * @throws MovieException
  *    若无影片类型则抛异常。
  */
 public void setPrice(String movieClass) throws MovieException {
  try {
   Class cls = Class.forName(movieClass);
   this.price = (Price) cls.newInstance();
  } catch (Exception e) {
   throw new MovieException("影片不存在");
  }
 }
}

给出MovieException源码。

package com.qujingbo.movie;

/**
 * <p/> Title:自定义异常
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 19:21:08
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
public class MovieException extends Exception {
 public MovieException(String msg) {
  super(msg);
 }
}

下面模访一个顾客租赁影片。

package com.qujingbo.movie;

/**
 * <p/> Title:
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 19:26:23
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
public class Customer {
 /**
  * 消费(测试程序)
  *
  * @throws MovieException
  *    若没有影片,抛出异常
  */
 public void consume() throws MovieException {
  // 普通电影
  Movie regularMovie = new Movie();
  regularMovie.setPrice(Movie.REGULAR);
  // 最新发布电影
  Movie newReleaseMovie = new Movie();
  newReleaseMovie.setPrice(Movie.NEW_RELEASE);
  // 儿童电影
  Movie childrenMovie = new Movie();
  childrenMovie.setPrice(Movie.CHILDREN);

  System.out.println("普通影片租赁10天的价格"
    + regularMovie.getPrice().getCharge(10));
  System.out.println("最新影片租赁10天的价格"
    + newReleaseMovie.getPrice().getCharge(10));
  System.out.println("儿童影片租赁10天的价格"
    + childrenMovie.getPrice().getCharge(10));

  System.out.println("普通影片租赁10天的积分"
    + regularMovie.getPrice().getIntegral(10));
  System.out.println("最新影片租赁10天的积分"
    + newReleaseMovie.getPrice().getIntegral(10));
  System.out.println("儿童影片租赁10天的积分"
    + childrenMovie.getPrice().getIntegral(10));

 }
}

写一 junit 测试类运行 class Customer 的 consume() 方法。

 package com.qujingbo.movie;

 import junit.framework.TestCase;

 /**
 * <p/> Title:junit测试类
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 19:32:57
 * </p>
 *
 * @author EOMS 曲静波
 * @version 1.0
 */
 public class CustomerTest extends TestCase {

  private Customer customer = null ;

  protected void setUp() throws Exception {
   super .setUp();
  customer = new Customer();
 }

  protected void tearDown() throws Exception {
   super .tearDown();
 }

  /*
  * Test method for 'com.qujingbo.movie.Customer.consume()'
  */
  public void testConsume() {
   try {
   customer.consume();
  } catch (MovieException e) {
   System.out.println( " 没有该类影片 " );
  }
 }

}

OK 。结果为:

普通影片租赁 10 天的价格 14.0

最新影片租赁 10 天的价格 30.0

儿童影片租赁 10 天的价格 12.0

普通影片租赁 10 天的积分 20.0

最新影片租赁 10 天的积分 30.0

儿童影片租赁 10 天的积分 15.0

最后我要说,我们用 OO 表示的租赁过程并不完整,因为顾客不一定只租赁一部影片,而要租赁多部影片,这样我们缺少一个 Rental (租赁类)。而只是为说明 state pattern 替代 if else ,所以我们没有添加 Rental (租赁类),若需要参考,请查阅《重构》第一章。 点击下载源码.

到此这篇关于Java如何利用状态模式(state pattern)替代if else的文章就介绍到这了,更多相关Java用状态模式(state pattern)替代if else内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java基础教程_判断语句if else

    与三元运算符相比: 好处:可以简化if else 代码 弊端 因为是一个运算符,所以运算玩必须要有一个结果 以上这篇Java基础教程_判断语句if else就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • Java编程细节重构之为什么if-else不是好代码详析

    前言 面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断.如果你还在大量使用if else,当然,界面表现层除外,即使你使用Java/C#这样完全面向对象的语言,也只能说明你的思维停留在传统的面向过程语言上.本文将通过示例代码给大家介绍关于Java编程细节重构之if-else的相关内容,下面来一起看看详细的介绍吧 平时开发中if-else用的多吗? 其实这是个再正常不过的coding习惯,当我们代码量小的时候用来做条件判断是再简单不过的了. 但对于优秀程序员来说

  • Java中if...else语句使用的学习教程

    if语句 一个if语句包含一个布尔表达式和一条或多条语句. 语法 If语句的用语法如下: if(布尔表达式) {    //如果布尔表达式为true将执行的语句 } 如果布尔表达式的值为true,则执行if语句中的代码块.否则执行If语句块后面的代码. public class Test { public static void main(String args[]){ int x = 10; if( x < 20 ){ System.out.print("这是 if 语句");

  • 如何优雅的替换掉Java代码中的if else

    场景 平时我们在写代码时,需要针对不同情况处理不同的业务逻辑,用得最多的就是if和else. 但是如果情况太多,就会出现一大堆的"if else",这就是为什么很多遗留系统中,一个函数可能出现上千行的代码.当然你说可以通过抽取方法或者类来实现,每一个情况交给一个方法或者对应一个类来处理,但是这样做只是看起来代码整洁了一些,还是有大量的"if else",后面有新的逻辑时,又要添加更多的"if else",没有从根本上解决问题. 举个例子,短信发送

  • 详解Java编程中if...else语句的嵌套写法

    if...else if...else语句 if语句后面可以跟elseif-else语句,这种语句可以检测到多种可能的情况. 使用if,else if,else语句的时候,需要注意下面几点: if语句至多有1个else语句,else语句在所有的elseif语句之后. If语句可以有若干个elseif语句,它们必须在else语句之前. 一旦其中一个else if语句检测为true,其他的else if以及else语句都将跳过执行. 语法 if...else语法格式如下: if(布尔表达式 1){

  • 聊聊java中一些减少if else 的编码习惯的方法

    前言 前段时间在阅读别人所写的代码的时候 , 发现其中一些业务相关的方法体内 , 出现了比较多的if-else语句多层嵌套的情况  . 首先我个人不是不提倡写if-else语句 , 不得不说 , 很多时候 , 在写某些逻辑 使用if-else 去做判断 , 代码看起来还是十分直观的  , 但是如果滥用if-else , 形成多层嵌套或者形成, 其中每个case 还包含了大量的逻辑 , 此时从可读性来说 , 使用if-else就有点得不偿失了 .  而且某些时候 ,  可能并不需这么多的if-el

  • Java的Struts框架中的if/else标签使用详解

    这些标签执行可在每一种语言找到的一种基本条件流程. 'If'标签可用于本身或与"Else If''标签和/或单/多'Else'标签,如下图所示: <s:if test="%{false}"> <div>Will Not Be Executed</div> </s:if> <s:elseif test="%{true}"> <div>Will Be Executed</div>

  • Java利用策略模式优化过多if else代码

    前言 不出意外,这应该是年前最后一次分享,本次来一点实际开发中会用到的小技巧. 比如平时大家是否都会写类似这样的代码: if(a){ //dosomething }else if(b){ //doshomething }else if(c){ //doshomething } else{ ////doshomething } 条件少还好,一旦 else if 过多这里的逻辑将会比较混乱,并很容易出错. 比如这样: 摘自cim中的一个客户端命令的判断条件. 刚开始条件较少,也就没管那么多直接写的:

  • java的if else语句入门指南(推荐)

    条件语句,是程序中根据条件是否成立进行选择执行的一类语句,这类语句在实际使用中,难点在于如何准确的抽象条件.例如实现程序登录功能时,如果用户名和密码正确,则进入系统,否则弹出"密码错误"这样的提示框等. 本部分对于条件语句的介绍,重点在于语法讲解和基本的使用,更详细的使用参看后续的综合示例部分. 在Java语言中,条件语句主要有两类语法:if语句和switch语句. 1 .if语句 if关键字中文意思是如果,其细致的语法归纳来说总共有三种:if语句.if-else语句和if-else

  • java如何消除太多的if else判断示例代码

    1.简介 if判断语句是很多编程语言的重要组成部分.但是,若我们最终编写了大量嵌套的if语句,这将使得我们的代码更加复杂和难以维护. 让我们看看能否使用别的方式来做呢. 设计模式是为了更好的代码重用性,可读性,可靠性,可维护性,它有六大原则: 单一职责原则(Single Responsibility Principle,简称SRP):该原则是针对类来说的,即一个类应该只负责一项职责. 开放--封闭原则(The Open-Closed Principle简称OCP):是说软件实体(类.模块.函数等

随机推荐