大多数开发人员现在还在使用if else的过程结构,曾看过jdon的banq大哥写的一篇文章,利用command,aop模式替代if else过程结构。当时还不太明白,这几天看了《重构》第一章的影片租赁案例,感触颇深。下面我来谈一谈为什么要用state pattern替代if else,替代if else有什么好处,以及给出详细代码怎么替代if else。本文参考jdon的“你还在使用if else吗?”及《重构》第一章。
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?>
首先我们模仿影片租赁过程,顾客租凭影片,影片分为儿童片、普通片、新片。根据影片类型及租凭天数价格各不相同(优惠程度不同),用户累计积分不同。
OK ,现在我们使用 if else 表示。
package
com.qujingbo.movie;
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
/** */
/**
* <p/> Title:影片基类
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:47:55
* </p>
*
* @author EOMS 曲静波
* @version 1.0
*/
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
public
class
Movie
{
![InBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
// 普通片标识
public static int REGULAR = 1 ;
// 新片标识
public static int NEW_RELEASE = 2 ;
// 儿童片标识
public static int CHILDREN = 3 ;
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
/** */ /**
* 获取租赁影片总价
*
* @param movieCode
* 影片类型
* @param days
* 租凭天数
* @return 租赁影片总价
* @throws MovieException
* 没有影片类型抛出异常
*/
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
public double getCharge( int movieCode, int days) throws MovieException
{
double result = 0 ;
// 普通片
if (movieCode == Movie.REGULAR)
// 单价为2
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
{
result = 2 ;
// 如果租赁天数大于2则,则优惠
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if (days > 2 )
{
result += (days - 2 ) * 1.5 ;
}
// 返回总价
return result;
}
// 最新发布片
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
else if (movieCode == Movie.NEW_RELEASE)
{
// 新片没有优惠,单价为3
return days * 3 ;
}
// 儿童片
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
else if (movieCode == Movie.CHILDREN)
{
// 影片单价
result = 1.5 ;
// 如果租赁时间大于3天则做价格优惠
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if (days > 3 )
{
result += (days - 3 ) * 1.5 ;
}
// 返回租赁影片总价
return result;
} else
throw new MovieException( " 影片不存在 " );
}
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
/** */ /**
* 获取租赁影片积分
*
* @param movieCode
* 影片类型
* @param days
* 租凭天数
* @return 租赁影片积分
* @throws MovieException
* 没有影片类型抛出异常
*/
public double getIntegral( int movieCode, int days) throws MovieException
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
{
// 普通片
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;
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
/** */
/**
* <p/> Title:
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:48:22
* </p>
*
* @author EOMS 曲静波
* @version 1.0
*/
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
public
abstract
class
Price
{
![InBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
/** *//**
* 获取租赁影片价格需实现该此方法
*
* @param days
* 租赁天数
* @return 返回影片价格
*/
public abstract double getCharge(int days);
![InBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
/** *//**
* 获取租赁影片积分需实现此方法
*
* @param days
* 租赁天数
* @return 返回影片积分
*/
public abstract double getIntegral(int days);
![InBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
}
儿童片
ChildrenPrice
类,实现
abstract class Price
,实现儿童片租赁总价
getCharge(int days)
及儿童片租赁积分
getIntegral(int days)
。
package
com.qujingbo.movie;
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
/** */
/**
* <p/> Title:儿童片租赁积分、价格实现
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:49:04
* </p>
*
* @author EOMS 曲静波
* @version 1.0
*/
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
public
class
ChildrenPrice
extends
Price
{
![InBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
/** *//**
* 儿童片返回租赁积分,儿童片积分规则为: 根据
*/
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
public double getIntegral(int days)
{
// 返回租赁影片积分
return days * 1.5;
}
![InBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
/** *//**
* 儿童片返回租赁价格
*/
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
public double getCharge(int days)
{
// 影片单价
double result = 1.5;
// 如果租赁时间大于3天则做价格优惠
![ExpandedSubBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
if (days > 3)
{
result += (days - 3) * 1.5;
}
// 返回租赁影片总价
return result;
}
![InBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
}
普通片RegularlPrice类,实现abstract class Price ,实现普通片租赁总价getCharge(int days)及普通片租赁积分getIntegral(int days)。
package
com.qujingbo.movie;
![None.gif](https://i-blog.csdnimg.cn/blog_migrate/4f1150b881333f12a311ae9ef34da474.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
/** */
/**
* <p/> Title:普通片租赁积分、价格实现
* </p>
* <p/> Description:
* </p>
* <p/> Date:2006-10-14 15:50:10
* </p>
*
* @author EOMS 曲静波
* @version 1.0
*/
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
public
class
RegularlPrice
extends
Price