策略模式
> 将算法分别封装起来,让它们可以相互替换,此模式让算法的变化不会影响到其调用者
实现一个简单的收银功能
class Program { public function calculate($price, $num) { // 实际项目中要考虑浮点计算时精度丢失的问题 $totalPrice = $price * $num; echo '单价'. $price .',数量'. $num .',总价:'. $totalPrice ."\n"; return $totalPrice; } } $total = 0; $program = new Program(); $total += $program->calculate(5, 2); $total += $program->calculate(12, 3); echo '总计:'. $total; /** 输出结果: 单价5,数量2,总价:10 单价12,数量3,总价:36 总计:46 **/
看起来已经满足需求,但是如果功能继续扩展,需要对某些商品进行打折
class Program
{
public function calculate($price, $num, $discount = 0)
{
$proportion = 1;
switch ($discount) {
case 8:
echo '享受8折,';
$proportion = 0.8;
break;
case 5:
echo '享受5折,';
$proportion = 0.5;
break;
default:
# code...
break;
}
$totalPrice = $price * $num * $proportion;
echo '单价'. $price .',数量'. $num .',总价:'. $totalPrice ."\n";
return $totalPrice;
}
}
$total = 0;
$program = new Program();
$total += $program->calculate(5, 2);
$total += $program->calculate(12, 3, 8);
$total += $program->calculate(12, 3, 5);
echo '总计:'. $total;
/**
输出结果:
单价5,数量2,总价:10
享受8折,单价12,数量3,总价:28.8
享受5折,单价12,数量3,总价:18
总计:56.8
**/
假如需要更多的折扣、满减、购买赠送积分,那么这里明显不能这么编写,总不可能一个折扣就新增一个 case
单纯使用工厂模式并不能解决问题,应当使用策略模式,把相同的算法归纳在一个策略里
例如不管打几折,都算是一种策略,满减和普通支付是另外的策略
建立一个抽象类 CashSuper 类,约束继承的子类(每个策略)都实现自己的计算方式 acceptCach
建立一个 CashContext 类,用来维护引用的对象,CashContext 类的构造方法传递进来的是抽象类 CashSuper 类型
而不是某个子类类型,而我们的子类都实现了计算方法,只需要传不同的子类就可以实现不同的策略
// 抽象类
abstract class CashSuper
{
// 约束子类实现计算方法
abstract function acceptCach($price, $num);
}
// 正常支付策略
class CashNormal extends CashSuper
{
public function acceptCach($price, $num)
{
return $price * $num;
}
}
// 折扣策略
class CashRebate extends CashSuper
{
public $discount; // 折扣
public function __construct($discount = 10)
{
$this->discount = $discount / 10;
}
public function acceptCach($price, $num)
{
return $price * $num * $this->discount;
}
}
// 满减策略
class CashRereturn extends CashSuper
{
public $flag; // 满
public $return; // 减
public function __construct($flag, $return)
{
$this->flag = $flag;
$this->return = $return;
}
public function acceptCach($price, $num)
{
$money = $price * $num;
if ($money >= $this->flag) {
echo 1;
$money = $money - (intval($money / $this->flag) * $this->return);
}
return $money;
}
}
// 此类维护我们的策略
class CashContext
{
public $obj = null;
// 这里传递进来的不是某个策略,而是抽象类类型,因为所有策略都继承于抽象类并实现了计算方法
function __construct(CashSuper $obj)
{
$this->obj = $obj;
}
// 根据传递进来的策略,调用其本身的计算方法
public function getResult($price, $num)
{
return $this->obj->acceptCach($price, $num);
}
}
class Program
{
public function calculate($price, $num, $type = 1, $discount = 0)
{
switch ($type)
{
case 1:
$obj = new CashNormal();
break;
case 2:
echo '享受'. $discount .'折,';
$obj = new CashRebate($discount);
break;
case 3:
echo '享受满减,';
$obj = new CashRereturn(300, 100);
break;
default:
# code...
break;
}
$totalPrice = (new CashContext($obj))->getResult($price, $num);
echo '单价'. $price .',数量'. $num .',总价:'. $totalPrice ."\n";
return $totalPrice;
}
}
$total = 0;
$program = new Program();
$total += $program->calculate(155, 2);
$total += $program->calculate(102, 3, 2, 8);
$total += $program->calculate(220, 3, 3);
echo '总计:'. $total;
/**
输出结果:
单价155,数量2,总价:310
享受8折,单价102,数量3,总价:244.8
享受满减,单价220,数量3,总价:460
总计:1014.8
**/