<?php /** * 接下来看的是策略模式,举得例子是商场搞活动 * 商场搞活动中需要不停的变化促销方式,有打折啦,满减啦,积分啦等等,而且打折力度也不一样,积分也不一样 * 所以就需要使用策略模式,将变化封装。策略模式的主要特点就是封装变化,那为什么不使用工厂模式呢, * 每一次有活动我在工厂中新增一个选项,然后实例化一个新的类,这样不也是可以么。那就下了就对比一下两个模式的不一样, * 看看为什么使用策略模式 */ // 先从最基础的构建开始,实现一个最简单的功能,不需要考虑封装继承等 // 服务端 $total = 0; function shop($price, $commodity) { $total = round($price * $commodity, 2); return $total; } // 客户端 $total = shop(1.2, 2); echo $total;
<?php // 上述实现了正常销售情况,那接下来经理需要搞活动,要给商品打折,有不同的打折力度 // 服务端 $total = 0; // 0: 五折, 1:8折, 2:9折 function shop($price, $commodity, $type) { switch ($type) { case '0': $total = round($price * $commodity, 2) * 0.5; break; case '1': $total = round($price * $commodity, 2) * 0.8; break; case '2': $total = round($price * $commodity, 2) * 0.9; break; default: return 'error'; } return $total; } // 客户端 $total = shop(1.2, 2, 1); echo $total; /** * 新增加了打折力度后,服务端需要修改的地方就比较多,如果现在经理又需要添加满减的活动,有的要满减,有的要在打折的基础上满减 * 如果按照当前的架构来实现的话,只能是在switch中不断的添加新的选项,同时要和客户端约定好0是什么,1是什么等等,如果商场开了5年 * 经理搞活动搞了2,300次,而且每次还搞的不一样,那这个代码将变的很庞大,很不好维护,而且客户端与服务端对于type的约定也会导致bug的产生 * 因此此处就需要使用面向对象的三大特性,之前还学习了简单工厂模式,正好用来实现这个功能 * 实现思路: * 1,将折扣封装成一个抽象类, * 2,然后不同的折扣规则继承这个抽象类 * 3,新建一个创建工厂类,然后根据不同的打折规则,实例化不同的折扣规则 */
<?php // 抽象收费基类 abstract class CashSuper{ public abstract function acceptCash($money); } // 正常收费类 class CashNormal extends CashSuper { public function acceptCash($money) { return $money; } } // 打折收费类 class CashRebate extends CashSuper { private $moneyRebate = 1; public function __construct($moneyRebate) { $this->moneyRebate = $moneyRebate; } public function acceptCash($money) { // TODO: Implement acceptCash() method. return $money * $this->moneyRebate; } } // 满减活动类 class CashReturn extends CashSuper { // 满减活动需要有满减的条件和满减的值,输入300,100就是满300减去100 private $moneyCondition = 0; private $moneyReturn = 0; public function __construct($moneyCondition, $moneyReturn) { $this->moneyCondition = $moneyCondition; $this->moneyReturn = $moneyReturn; } public function acceptCash($money) { $result = $money; if ($money >= $this->moneyCondition) { $result = $money - floor($money / $this->moneyCondition) * $this->moneyReturn; } return $result; } } // 创建工厂内,实现自动实例化对象 class CashFactory { private $cs = null; public function cteateCashAccept($type) { switch ($type) { case '正常收费': $this->cs = new CashNormal(); break; case '打折': $this->cs = new CashRebate(0.8); break; case '满300减100': $this->cs = new CashReturn(300, 100); break; default: return 'error'; } return $this->cs; } } // 客户端 $csf = new CashFactory(); $cs = $csf->cteateCashAccept('正常收费'); $res = $cs->acceptCash(100); echo $res; $cs = $csf->cteateCashAccept('打折'); $res = $cs->acceptCash(100); echo $res; $cs = $csf->cteateCashAccept('满300减100'); $res = $cs->acceptCash(600); echo $res; /** * 上述方法是使用简单工厂实现的,但是还是具有缺点的,比较之前实现计算器的时候, * 计算器的规则是固定的,不会频繁的改动,但是在此处这些促销算法是不断更新的 */
<?php /** * 针对上述问题,现在就可以引出策略模式 * 先看到策略模式的概念: * 策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户 * 接下来使用策略模式实现此功能,然后参照代码理解上边的概念 */ // 抽象收费基类 abstract class CashSuper{ public abstract function acceptCash($money); } // 正常收费类 class CashNormal extends CashSuper { public function acceptCash($money) { return $money; } } // 打折收费类 class CashRebate extends CashSuper { private $moneyRebate = 1; public function __construct($moneyRebate) { $this->moneyRebate = $moneyRebate; } public function acceptCash($money) { // TODO: Implement acceptCash() method. return $money * $this->moneyRebate; } } // 满减活动类 class CashReturn extends CashSuper { // 满减活动需要有满减的条件和满减的值,输入300,100就是满300减去100 private $moneyCondition = 0; private $moneyReturn = 0; public function __construct($moneyCondition, $moneyReturn) { $this->moneyCondition = $moneyCondition; $this->moneyReturn = $moneyReturn; } public function acceptCash($money) { $result = $money; if ($money >= $this->moneyCondition) { $result = $money - floor($money / $this->moneyCondition) * $this->moneyReturn; } return $result; } } // 原先封装好的具体活动规则视为3个策略 // 接下来使用策略模式的 context 上下文处理类,就像简单工厂模式具有创建工厂类一样 class CashContext { private $cs = null; // 使用构造方法,在实例化此类的时候,就需要将使用的策略传递进来 public function __construct(CashSuper $cs) { $this->cs = $cs; } public function getResult($money) { return $this->cs->acceptCash($money); } } // 客户端实现 function shop($type, $money) { $cs = null; switch ($type) { case '正常收费': $cs = new CashContext(new CashNormal()); break; case '满300减100': $cs = new CashContext(new CashReturn(300, 100)); break; case '打折': $cs = new CashContext(new CashRebate(0.8)); break; } $res = $cs->getResult($money); return $res; } $res = shop('满300减100', 600); echo $res; /** * 此处使用了策略模式,使用了上下文处理类,调用者需要使用哪种策略将对应的策略传给进来就好 * 到此处就有些懵逼,这策略模式和简单工厂模式到底有什么区别啊,而且还把判断逻辑移动到客户端去处理 * 难道每个使用此类的方法都自己去判断啊? * * 如果这样思考那就片面了,并不是要去对比两个模式哪个好哪个坏,而且每当碰到这种需求的时候,要学会使用哪种模式去解决当前遇到的问题 * 此处使用策略模式,可以很好的将具体算法和客户端进行了隔离,在未使用策略模式的时候,需要客户端自己去调用算法的实现方法, * 这样做的好处是什么呢,就是降低耦合性,像在此处客户端只接触到了CashContext类,以及他认为的算法实现方法getResult; * 这都是属于CashContext类的,但是在工厂模式中就需要使用到具体算法的实现方法acceptCash。 * 我们使用策略模式很好的进行了封装隔离,只将上下文处理类中的一个方法暴露给客户端,大大降低了耦合性,客户端升级,服务端升级改动等都可以 * 很轻松的进行。那此处应当理解的就是封装变化,封装变化可以降低耦合性。 */ // 但是此处将判断逻辑放到客户端是不好的,那解决方案就是结合简单工厂模式,将策略模式有简单工厂模式结合使用
<?php // 抽象收费基类 abstract class CashSuper{ public abstract function acceptCash($money); } // 正常收费类 class CashNormal extends CashSuper { public function acceptCash($money) { return $money; } } // 打折收费类 class CashRebate extends CashSuper { private $moneyRebate = 1; public function __construct($moneyRebate) { $this->moneyRebate = $moneyRebate; } public function acceptCash($money) { // TODO: Implement acceptCash() method. return $money * $this->moneyRebate; } } // 满减活动类 class CashReturn extends CashSuper { // 满减活动需要有满减的条件和满减的值,输入300,100就是满300减去100 private $moneyCondition = 0; private $moneyReturn = 0; public function __construct($moneyCondition, $moneyReturn) { $this->moneyCondition = $moneyCondition; $this->moneyReturn = $moneyReturn; } public function acceptCash($money) { $result = $money; if ($money >= $this->moneyCondition) { $result = $money - floor($money / $this->moneyCondition) * $this->moneyReturn; } return $result; } } // 原先封装好的具体活动规则视为3个策略 // 接下来使用策略模式的 context 上下文处理类,就像简单工厂模式具有创建工厂类一样 class CashContext { private $cs = null; // 使用构造方法,在实例化此类的时候,就需要将使用的策略传递进来 public function __construct($type) { switch ($type) { case '正常收费': $cs = new CashNormal(); break; case '满300减100': $cs = new CashReturn(300, 100); break; case '打折': $cs = new CashRebate(0.8); break; } $this->cs = $cs; } public function getResult($money) { return $this->cs->acceptCash($money); } } // 客户端实现 function shop($type, $money) { $cs = new CashContext($type); $res = $cs->getResult($money); return $res; } $res = shop('满300减100', 600); echo $res; /** * 此处将策略模式与简单工厂模式的结合使用分别于策略模式,简单工厂模式进行比对 * * 与简单工厂模式比对:其实就是增加了一个地方 getResult这个方法,而这个方法就是策略模式的使用,就是使用这个方法,将客户端与实际的 * 算法类进行了隔离,封装了变化(算法),降低了耦合性。我客户端只让定你这个CashContext类中的这个方法啊就可以了,和你没有其他任何的关联 * 对于不同的算法来说,我编写我的算法就好了,只要我测试我的算法接受正常的参数时可以正常运行,那就可以了 * * 与策略模式比对:将判断实例化哪个类放到工厂模式的方法中进行,客户端需要做的只是传递参数即可,工厂方法会根据具体的参数判断出该实例化哪个类 * 这样也大大降低了服务端与客户端的耦合性 */