使用state pattern替代if else

Java 专栏收录该内容
35 篇文章 0 订阅

大多数开发人员现在还在使用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 。先来个类图。

 

ifelse.jpg  

首先我们建立一个 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 (租赁类),若需要参考,请查阅《重构》第一章。 点击下载源码 .

 

这是我第一次写技术文章,如果有适当的地方,请各位朋友提出各自见解。

  • 1
    点赞
  • 5
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值