六、锦囊妙计--策略模式
1、恐龙函数
我们看一个很简单的电子商务网站的功能:
1、普通会员,商品不打折。
2、高级会员,商品打9折。
3、vip会员,商品打8折。
我们可能会这样设计代码:
例:6_1
<?php
//打折类
class discountor
{
public function getDiscount($rank)
{
switch ($rank) {
case 'common':
$discount = 1;
break;
case 'advance':
$discount = 0.9;
break;
case 'vip':
$discount = 0.8;
break;
}
return $discount;
}
}
$discountor = new discountor();
echo $discountor->getDiscount('advance');
?>
现在由于每个等级的折扣,不需要计算,所以看起来还是很简单的。
如果$discount不是那么简单出来的,需要分析,比如最近我们给vip的折扣是否有促销,或者这款产品本身是否有特别优惠。
那么这个代码将非常长,这个getDiscount函数的代码也会超长。
等写好这个类的代码,你会发现似乎这不是一个类,而是一个巨大的函数。
我们定义了一个恐龙般巨大的函数,如果一旦不同等级的计算方法发生变动,这个getDiscount函数的修改将是一场噩梦。
2、恐龙类
前面我们搞了一个巨型函数,我们先设法优化这个函数的代码,至少便于修改这个函数。
其实我们分析这个函数,由于switch,其实分三个情况来计算折扣,那么我们想到将三个情况的计算,分到不同的函数。
例:6_2
<?php
//打折类
class discountor
{
public function getDiscount($rank)
{
switch ($rank) {
case 'common':
$discount = $this->getCommonDiscount();
break;
case 'advance':
$discount = $this->getAdvanceDiscount();
break;
case 'vip':
$discount = $this->getVipDiscount();
break;
}
return $discount;
}
protected function getCommonDiscount()
{
$discount = 1;
return $discount;
}
protected function getAdvanceDiscount()
{
$discount = 0.9;
return $discount;
}
protected function getVipDiscount()
{
$discount = 0.8;
return $discount;
}
}
$discountor = new discountor();
echo $discountor->getDiscount('advance');
?>
这样一来,我们只要修改相关的函数,就可以完成某种级别用户的折扣计算。
不过还是有个问题,这个类太大了,几个和级别相关的函数里面承担了太大的业务处理。
我们消灭了一个恐龙般的函数,但是这个类还是恐龙类。
3、锦囊妙计
前面我们分拆函数getDiscount的时候,已经有了思路,就是拆开一些代码来简化。
在三国演义中,赵云身上带着的锦囊,在不同情况下拆开来解决问题,其实就是封装的思路。
我们不必把discountor当作锦囊,而是把他当作赵云,做几个锦囊类来给赵云用。
例:6_3
<?php
//打折接口
interface IDiscountor
{
public function getDiscount();
}
//普通用户打折类
class commonDiscountor implements IDiscountor
{
public function getDiscount()
{
$discount = 1;
return $discount;
}
}
//高级用户打折类
class advanceDiscountor implements IDiscountor
{
public function getDiscount()
{
$discount = 0.9;
return $discount;
}
}
//vip用户打折类
class vipDiscountor implements IDiscountor
{
public function getDiscount()
{
$discount = 0.8;
return $discount;
}
}
//打折类
class discountor
{
public function getDiscount($rank)
{
switch ($rank) {
case 'common':
$discountor = new commonDiscountor();
break;
case 'advance':
$discountor = new advanceDiscountor();
break;
case 'vip':
$discountor = new vipDiscountor();
break;
}
return $discountor->getDiscount();
}
}
$discountor = new discountor();
echo $discountor->getDiscount('advance');
?>
这个就是策略模式(Strategy),将不同情况的算法分拆到不同的类(比如advanceDiscountor类),然后调用者(discountor类)根据具体的情况,用算法类的对象来处理。
思考:
策略模式是一种常用的模式,我们可以用它来封装不同情况下的算法代码,方便修改。
但是策略模式,如果乱用,那么就会出来很多多余的算法类,也就是分拆过度。
另外,策略类一般直接用new来新建对象调用算法,他与类名是用比较高的关联度,所以有时候,需要考虑用工厂或其他方法来进一步优化。
备注:本文涉及代码,在《phper》第八期中。另外在上海一月份的活动中,我对策略模式进行了更深入的探讨,具体资料请关注论坛pea版块的上海一月份活动总结贴。