本文已参与「新人创作礼」活动,一起开启掘金创作之路。
案例:商场收银软件
小菜的程序
class Program {
public function getResult($totalPrice, $goodsNumber, $selectIndex)
{
$_totalPrice = 0;
switch ($selectIndex)
{
case 0: //正常收费
$_totalPrice = $totalPrice * $goodsNumber;
break;
case 1: //打八折
$_totalPrice = $totalPrice * $goodsNumber * 0.8;
break;
case 2: //打七折
$_totalPrice = $totalPrice * $goodsNumber * 0.7;
break;
case 3: //打五折
$_totalPrice = $totalPrice * $goodsNumber * 0.5;
break;
}
return $_totalPrice;
}
}
$program = new Program();
echo $program->getResult(50, 3, 1);
简单工厂模式代码:
//现金收费抽象类
abstract class CashSuper {
//收取现金抽象方法,参数为原价,返回为当前价
public abstract function acceptCash($money);
}
//正常收费子类
class CashNormal extends CashSuper {
public function acceptCash($money)
{
// TODO: Implement acceptCash() method.
return $money;
}
}
//打折收费子类
class CashRebate extends CashSuper {
private $moneyRebate = 1;
//初始化的时候输入折扣费率,如八折为0.8
public function __construct($moneyRebate)
{
$this->moneyRebate = $moneyRebate;
}
public function acceptCash($money)
{
// TODO: Implement acceptCash() method.
return $money * $this->moneyRebate;
}
}
//返利收费子类
class CashReturn extends CashSuper {
private $moneyCondition = 0;
private $moneyReturn = 0;
//初始化的时候输入返利条件和返利值,如满300返100为 $moneyCondition=300,$moneyReturn=100
public function __construct($moneyCondition, $moneyReturn)
{
$this->moneyCondition = $moneyCondition;
$this->moneyReturn = $moneyReturn;
}
public function acceptCash($money)
{
// TODO: Implement acceptCash() method.
if($money > $this->moneyCondition)
{
return $money - floor($money/$this->moneyCondition) * $this->moneyReturn;
}
}
}
//现金收费工厂类
class CashFactory {
public function createCashAccept($type)
{
$cs = '';
//根据条件实例化返回相应的对象
switch ($type)
{
case '正常收费':
$cs =new CashNormal();
break;
case '满300返100':
$cs =new CashReturn(300, 100);
break;
case '打八折':
$cs =new CashRebate(0.8);
break;
}
return $cs;
}
}
//客户端代码
$cashFactory = new CashFactory();
$cs = $cashFactory->createCashAccept('满300返100');
echo $cs->acceptCash(70*10);
策略模式代码:
由于返利的算法会时常变动,所以要使用策略模式 这样上面的代码中CashSuper就是抽象策略,而正常收费 CashNormal、 打折收费 CashRebate 和返利收费 CashReturn 就是三个具体策略,也就是策略模式中说的具体算法。 这个几个类不需要更改,添加CashContext 类来接受具体的策略对象,调用其算法的方法。
//CashContext类
class CashContext {
//声明一个CashSuper对象
private $cs;
//初始化传入具体的收费策略
public function __construct(CashSuper $csuper)
{
$this->cs = $csuper;
}
public function getResult($money)
{
return $this->cs->acceptCash($money);
}
}
//客户端代码
$cc = '';
$type = '打八折';
switch ($type)//根据条件将相应的策略模式对象传入CashContext的对象中
{
case '正常收费':
$cc =new CashContext(new CashNormal());
break;
case '满300返100':
$cc =new CashContext(new CashReturn(300, 100));
break;
case '打八折':
$cc =new CashContext(new CashRebate(0.8));
break;
}
echo $cc->getResult(700);
策略模式与简单工厂模式结合:
上边的客户端代码出现了判断使用哪一个算法的代码,使用策略模式与简单工厂模式结合来解决这一问题。 改造后的CashContext
//CashContext类
class CashContext {
//声明一个CashSuper对象
private $cs;
//初始化传入收费类型
public function __construct($type)
{
switch ($type)//根据条件将相应的策略模式对象传入CashContext的对象中
{
case '正常收费':
$this->cs = new CashNormal();
break;
case '满300返100':
$this->cs = new CashReturn(300, 100);
break;
case '打八折':
$this->cs = new CashRebate(0.8);
break;
}
}
public function getResult($money)
{
return $this->cs->acceptCash($money);
}
}
//客户端代码
$type = '满300返100';
$cashContext = new CashContext($type);
echo $cashContext->getResult(700);
注:策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性[DPE]。 在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象,这本身没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Contex来承担,这就最大化的减轻了客户端的职责。 但是依然不够完美,CashContext 里还是用到了switch代码,这让人很不爽,反射技术可以解决这一问题。