# 《大话设计模式》php版:策略模式
时间:2月27日22点 地点:大鸟房间 人物:小菜、大鸟
# 2.1 商场收银软件
“小菜,给你出个作业,做一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。”
“就这个?木问题。”小菜说,“用两个文本框来输入单价和数量,一个确定按键来算出每种商品的费用,用个列表框来记录商品的清单,一个标签来记录总计,对,还需要一个重置控制来重新开始,不就行了?!”
商场收银系统 v1.0 关键代码如下:
```php
<?php
//Cash.php
class Cash {
private $total = 0;
public function submit($num, $price)
{
$totalPrices = $num * $price;
$this->total += $totalPrices;
printf("单价:%s,数量:%s,合计:%s", $price, $num, $totalPrices);
}
}
$obj = new Cash();
$obj->submit(2,39);
?>
```
“大鸟,”小菜叫道,“来看看,这不就是你要的收银软件吗?我不到半个小时就搞定了啦。”
“哈哈,挺快的嘛。”大鸟说着,看了看小菜的代码。接着说:“现在我要求商场对商品搞活动,所有的商品打 8 折。”
“那不就是在 totalPrices 后面乘以个 0.8 吗?”
“小子,难道商场活动结束,不打折了,你还要再改一遍代码,然后再用改后的程序把所有的机器全部安装一次吗?再说,还有可能因为周年庆,打五折的情况,怎么办?”
小菜不好意思道:“啊,我想的是简单了点。其实呢,只要增加一个下拉菜单选项框就可以解决你说的问题啦。”
大鸟笑而不语。
# 2.2 增加打折
商场收银系统 v1.1 关键代码如下:
```php
<?php
//Cash.php
class Cash {
private $total = 0;
public function submit($rebateCondition, $num, $price)
{
$totalPrices = 0;
switch ($rebateCondition)
{
case "正常收费":
$totalPrices = $num * $price;
break;
case "打8折":
$totalPrices = $num * $price * 0.8;
break;
case "打7折":
$totalPrices = $num * $price * 0.7;
break;
case "打5折":
$totalPrices = $num * $price * 0.5;
break;
default:
$totalPrices = $num * $price;
break;
}
$this->total += $totalPrices;
printf("单价:%s,数量:%s,折扣方式:%s,合计:%s ", $price, $num, $rebateCondition, $totalPrices);
}
}
$obj = new Cash();
$obj->submit("打8折",2,39);
?>
```
“这下可以了吧,只要我事先把商场可能的打折都做成下拉菜单的样子,就可以了”小菜说道。
“这比刚才灵活性上是好了,不过重复代码很多,4 个分支语句除了打折多少不同以外几乎完全一样,应该考虑重构一下。不过这还不是最主要的,现在我的需求又来了,商场的活动加大,需要有满300返100的促销算法,你说该怎么办?”
“满 300 返 100,那要是 700 就要返 200 了?这个必须要写成函数了吧?”
“小菜啊,看来之前教你的都白教了,这里面看不出来什么名堂吗?”
“哦!我想起来了,你的意思是简单工厂模式,对对对,我可以先写一下父类,再继承它实现多个打折和反利的子类,复用多态性来完成这个代码。”
“那你打算写几个子类?”
“根据需求嘛,比如 8 折、7 折、5 折、满 300 送 100、满 200 送 50…要几个写几个。”
“小菜又不动脑子了,有必要这样写吗?如果我现在是 3 折,我要满 300 送 80,你难道再去增加子类?你不想想看,这当中哪些是相同的,哪些是不同的?”
# 2.3 简单工厂实现
大鸟:“对的,这里打折基本都是一样的,只要有个初始化参数就可以了。满几送几的,需要两个参数才行,明白,现在看来不麻烦了。”
大鸟:“面向对象的编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同的属性和功能的对象的抽象集合才是类。打1折和打9折只是形式不同,抽象分析出来,所有的打折算法都是一样的,所以打折算法应该是一个类。好了,空话说的太多了,写出来才是硬道理。”
大约1个小时后,小菜交出了第三份作业。
代码结构图
![代码结构图](http://hi.csdn.net/attachment/201006/19/0_1276989870q5S1.gif)
ICashSuper.php
```php
<?php
namespace Entere\Pattern\Strategy;
//现金收钱接口
interface ICashSuper {
/**
* 计算价钱
* @param double $money
* @return [type] [description]
*/
public function acceptCash($money);
}
?>
```
CashNormal.php
```php
<?php
namespace Entere\Pattern\Strategy;
use Entere\Pattern\Strategy\ICashSuper;
// 正常收钱策略
class CashNormal implements ICashSuper {
/**
* 返回商品正常价,好东西不打折
* @param [double] $money 商品正常价
* @return [double] 返回正常价
*/
public function acceptCash($money) {
return $money;
}
}
?>
```
CashRebate.php
```php
<?php
namespace Entere\Pattern\Strategy;
use Entere\Pattern\Strategy\ICashSuper;
// 折扣收取策略
class CashRebate implements ICashSuper {
private $_moneyRebate = 1; //折扣点数 0<$_moneyRebate<=1
public function __construct($moneyRebate) {
$this->_moneyRebate = $moneyRebate;
}
/**
* 计算折扣后的报价
* @param double $money 商品原价
* @return double 返回折扣后的报价
*/
public function acceptCash($money) {
return $this->_moneyRebate * $money;
}
}
?>
```
CashReturn.php
```php
<?php
namespace Entere\Pattern\Strategy;
use Entere\Pattern\Strategy\ICashSuper;
// 返利策略
class CashReturn implements ICashSuper {
private $_moneyCondition = 0;//返利条件
private $_moneyReturn = 0;//返利钱数
/**
* 比如满300减100
* @param double $moneyCondition 300
* @param double $moneyReturn 100
* @return
*/
public function __construct($moneyCondition, $moneyReturn) {
$this->_moneyCondition = $moneyCondition;
$this->_moneyReturn = $moneyReturn;
}
/**
* 返回满300减100后的金额
* @param double $money 原价
* @return double 满300减100后的金额
*/
public function acceptCash($money) {
$result = $money;
if ($money >= $this->_moneyCondition) {
$result = $money - intval($money / $this->_moneyCondition) * $this->_moneyReturn;
}
return $result;
}
}
?>
```
CashFactory.php
```php
<?php
//现金收费工厂类
namespace Entere\Pattern\Strategy;
use Entere\Pattern\Strategy\CashNormal;
use Entere\Pattern\Strategy\CashRebate;
use Entere\Pattern\Strategy\CashReturn;
class CashFactory {
public static function createCash($type)
{
$cs = null;
if ("正常收费" == $type) {
$cs = new CashNormal();
} else if ("满300返100" == $type) {
$cs = new CashReturn(300, 100);
} else if ("打8折" == $type) {
$cs = new CashRebate(0.8);
}
return $cs;
}
}
?>
```
Client.php
```php
<?php
//客户端代码
class Client
{
private $total = 0;
public function main()
{
$this->consume("正常收费", 1, 1000);
$this->consume("满300返100", 1, 1000);
$this->consume("打8折", 1, 1000);
printf("总计:%s", $total);
}