目的:
策略模式定义了算法族,分别封装起来,让他们之间可以相互替换。该模式让算法独立于使用它的客户而独立变化。
看到上面文字的同学可能有一点懵,设计模式的概述总是这么深奥和苦涩。
没事,我们来换个说法方便大家理解一下。
老规矩,还是以举例子来说,就拿老干妈吧。(迷之举例老干妈 o (╥﹏╥) o)
场景
举个例子,我们现在是老干妈的一个生产厂家,根据老干妈公司不同的需求生产不同的老干妈产品。
老干妈最基础的材料我们知道是什么对吧,相信大家都吃过,有豆豉(黄豆)
、辣椒
,可以根据老干妈公司的要求生产不同的口味
例如香笋味
、芽菜味
,先就简单举例这两个味道吧。
那么我们现在有可以确定的基础材料豆豉(黄豆)
、辣椒
对吧,不同口味只需要加入不同的材料:香笋
、芽菜
就可以生产出不一样的商品。
那么我们先按照以上思路来用代码实现,这么简单的需求大家一定会想到使用工厂模式
解决,只需要将固定材料作为基础父类,然后不同口味作为子类。
很简单吧!
工厂实现
老干妈基础材料父类:
- <?php
- /**
- * 老干妈基类
- */
- abstract class oldMother
- {
-
- // 黄豆
- public $soy;
-
- // 辣椒
- public $chili;
-
- // 口味
- public $taste;
-
- public function __construct()
- {
- $this->soy = '黄豆';
- $this->chili = '辣椒';
- }
-
- /**
- * 制造老干妈
- * @return string
- */
- public function manufacture()
- {
- return '制造老干妈:' . $this->soy . '+' . $this->chili . '!口味:' . $this->taste;
- }
-
- /**
- * 加入口味
- * 各自口味子类实现
- */
- public abstract function setTaste();
- }
-
竹笋味老干妈:
- /**
- * 竹笋味老干妈
- */
- class bambooShootsOldMother extends oldMother
- {
- /**
- * 加入香笋
- */
- public function setTaste()
- {
- $this->taste = '香笋';
- }
- }
芽菜味老干妈:
- /**
- * 芽菜味老干妈
- */
- class sproutsOldMother extends oldMother
- {
-
- /**
- * 加入芽菜
- */
- public function setTaste()
- {
- $this->taste = '芽菜';
- }
-
- }
老干妈生产工厂:
- /**
- * 老干妈生产工厂
- */
- class oldMotherFactory
- {
-
- /**
- * 生产老干妈,默认为芽菜口味
- * @param string $type
- */
- public function getOldMother(string $type = 'sprouts')
- {
- switch ($type) {
- case 'sprouts':
- return new sproutsOldMother();
- case 'bambooShoots':
- return new bambooShootsOldMother();
- }
-
- }
- }
测试生产:
- // 测试
- // 获得一个香笋味老干妈类
- $bambooShoots = (new oldMotherFactory())->getOldMother('bambooShoots');
- // 设置口味
- $bambooShoots->setTaste();
- // 制造
- var_dump($bambooShoots->manufacture());
-
- // 获得一个芽菜味老干妈类
- $sprouts = (new oldMotherFactory())->getOldMother('sprouts');
- // 设置口味
- $sprouts->setTaste();
- // 制造
- var_dump($sprouts->manufacture());
测试结果:
- string(49) "制造老干妈:黄豆+辣椒!口味:香笋"
- string(49) "制造老干妈:黄豆+辣椒!口味:芽菜"
执行结果似乎十分令人满意是吧,就这样,我们的老干妈生产工厂稳定的生产全国老干妈几个月,赚的盆满钵满。
可是......
由于大众对竹笋味的老干妈口味不太接受,偏爱芽菜味的老干妈,导致竹笋味老干妈和芽菜味老干妈销量差距过于悬殊,老干妈公司为此头疼不已!
但是就在昨天,腾讯公司找上老干妈公司,需要给公司旗下的 QQ 飞车待达成合作,给竹笋味
的老干妈的瓶子里印上 CDK
,这样吃竹笋味的老干妈就可以兑换 QQ 飞车的道具,这样以来竹笋味的老干妈销量增加了,到时候再跟腾讯一起分润,共赢!这场合作使老干妈公司非常开心(滑稽.jpg)
于是乎....
老干妈公司找到我们的生产公司,要求我们在生产老干妈的时候给瓶子里加上 CDK。
生产主管杰克马觉得这个需求非常简单,我们只需要在老干妈的基类里面加上 CDK
方法不就行了?
杰克马说干就干,马上修改了基类:
- <?php
- /**
- * 老干妈基类
- */
- abstract class oldMother
- {
-
- // 黄豆
- public $soy;
-
- // 辣椒
- public $chili;
-
- // 口味
- public $taste;
-
- // 给瓶子加上了cdk
- public $cdk;
-
- public function __construct()
- {
- $this->soy = '黄豆';
- $this->chili = '辣椒';
- $this->cdk = 'QQ飞车';
- }
-
- /**
- * 制造老干妈
- * @return string
- */
- public function manufacture()
- {
- return '制造老干妈:' . $this->soy . '+' . $this->chili . '!口味:' . $this->taste . ', CDK:' . $this->cdk;
- }
-
- /**
- * 加入口味
- * 各自口味子类实现
- */
- public abstract function setTaste();
- }
花了一首歌的时间,杰克马就改好了生产方法,开始生产带 CDK 的老干妈了,老干妈对于生产厂的效率似乎也感到十分满意,决定每瓶老干妈多给生产厂 3 毛钱提成。
杰克马为此开心不已。
但是好久不长....
由于杰克马直接在基类中加入了 CDK 属性,导致每个口味的老干妈都能获得 CDK,既然都能获得 CDK,用户都去买芽菜味的老干妈了,因为竹笋味的老干妈没有那么好吃,导致销量急剧下滑,也偏离了最初跟腾讯合作提高竹笋味销量的目的!
这下杰克马慌乱不已,这下不仅提成没着落,出这么大的事故给老干妈生产的机会都不知道还有没有。o (╥﹏╥) o
这个时候,手下的小王提出了一个解决方案:
- 我们应该把基类中的cdk属性去掉,放置在每个子类当中,如果子类需要给瓶子贴上CDK的话自己在类里面处理
杰克马顿时觉得小王说的十分有道理,不枉自己辛苦栽培小王,关键时刻还是没有掉链子。
于是乎,杰克马正准备补救开始着手修改的时候...
- 慢着!这个方案只能治标,不能治根
突然,另一个手下小方站了出来。
杰克马似乎跟小方十分不对付,平时不请他洗脚不上道也就算了,关键时刻还来耽误事?
杰克马不满的说道:are you ok? 闭嘴,小方,我正在跟小王商量补救大计,这里哪有你说话的份?
小王不紧不慢的说道:
- 马哥,你先听我把话说完!
- 老王的方案虽然目前能解燃眉之急,但是此法只能治标,不能治本。
- 我们试想一下,把cdk放到每个子类当中由子类自己去解决是否贴牌,看似十分简单,其实里面后患无穷。
- 就算我们解决了腾讯CDK问题,还有以下的问题等着我们:
- 1.万一哪天老干妈新出了很多口味,有牛肉,五香仁,韭菜味等,都需要贴CDK,只有芽菜不需要,那是不是我们除了芽菜其他子类都要去写一遍CDK的方法?
- 2.万一哪天老干妈跟阿里爸爸合作了,要在瓶子里面贴掏包的二维码呢?那我们是不是需要每个子类再去写个二维码方法?
- 3.这么多子类都有同一个方法代码,而只有贴的内容不同,那我们的代码是不是冗余严重了呢?
- 3.每增加一个口味都需要增加一个子类
- 我们必须要想一个设计上,代码上都可继续扩展的方法,而不是图一时之快只解决眼前问题!
杰克马听完顿时惊觉:雾草,牛批,想不到平日里如此普通的你竟有如此高见!那依你看?
小王继续说道:
- 首先我们可以确定老干妈的原材料黄豆和辣椒是不变的,这个叫“共同语言”。(如果原材料黄豆和辣椒变了那老干妈就可以直接倒闭了)
- 其次我们目前能确定的“不共同语言”有:合作的厂商、口味这两个。
- 那我们就把“不共同语言”部分抽象出来,组成老干妈的行为,然后动态的去生产,这样就不会因为老干妈的合作厂商和口味的不同去耦合冗余代码!
下面请看小王的实现!
策略模式
定义算法族(本文也称行为)
口味算法族:
- /**
- * 口味行为接口
- * Interface tasteBehavior
- */
- interface tasteBehavior
- {
- public function taste();
- }
-
- /**
- * 香笋口味
- * Class bambooShootsBehavior
- */
- class bambooShootsBehavior implements tasteBehavior
- {
- /**
- * 具体行为
- * @return string
- */
- public function taste()
- {
- return '香笋';
- }
- }
-
- /**
- * 芽菜口味
- * Class sproutsBehavior
- */
- class sproutsBehavior implements tasteBehavior
- {
- /**
- * 具体行为
- * @return string
- */
- public function taste()
- {
- return '芽菜';
- }
- }
合作公司算法族:
- /**
- * 厂商行为接口
- * Interface vendorBehavior
- */
- interface vendorBehavior
- {
- public function vendor();
- }
-
- /**
- * 疼迅公司
- */
- class tencentBehavior implements vendorBehavior
- {
-
- public function vendor()
- {
- return '腾讯:QQ飞车CDK';
- }
- }
-
- /**
- * 阿里爸爸
- */
- class alibabaBehavior implements vendorBehavior
- {
-
- public function vendor()
- {
- return '阿里爸爸:掏包二维码';
- }
- }
老干妈基类【环境角色】
- /**
- * 老干妈基类
- */
- class oldMother
- {
-
- // 黄豆
- public $soy;
-
- // 辣椒
- public $chili;
-
- /**
- * 口味行为
- * @var tasteBehavior
- */
- public $tasteBehavior;
-
- /**
- * 合作厂商行为
- * @var vendorBehavior
- */
- public $vendorBehavior;
-
- public function __construct()
- {
- $this->soy = '黄豆';
- $this->chili = '辣椒';
- }
-
- /**
- * 制造老干妈
- * @return string
- */
- public function manufacture()
- {
- return '制造老干妈:' . $this->soy . '+' . $this->chili . '!';
- }
-
- /**
- * 设置口味行为
- * @param tasteBehavior $tasteBehavior
- */
- public function setTasteBehavior(tasteBehavior $tasteBehavior)
- {
- $this->tasteBehavior = $tasteBehavior;
- }
-
- /**
- * 执行口味行为
- * @return string
- */
- public function doTasteBehavior()
- {
- return '口味:' . $this->tasteBehavior->taste();
- }
-
- /**
- * 设置合作企业行为
- * @param vendorBehavior $tasteBehavior
- */
- public function setVendorBehavior(vendorBehavior $tasteBehavior)
- {
- $this->tasteBehavior = $tasteBehavior;
- }
-
- /**
- * 执行合作企业行为
- * @return string
- */
- public function doVendorBehavior()
- {
- return '合作企业:' . $this->vendorBehavior->vendor();
- }
-
- }
动态生产测试:
- // 生产一个芽菜口味的老干妈,并且跟腾讯合作QQ飞车合作CDK
- $tencentOldMother = new OldMother();
- // 为腾讯老干妈加上芽菜口味和QQ飞车CDK
- $tencentOldMother->setTasteBehavior(new sproutsBehavior());
- $tencentOldMother->setVendorBehavior(new tencentBehavior());
- var_dump($tencentOldMother->manufacture()); // 开始生产
- var_dump($tencentOldMother->doTasteBehavior()); // 加入口味
- var_dump($tencentOldMother->doVendorBehavior()); // 加入合作企业
-
- var_dump('========================');
-
- // 生产一个香笋味老干妈,并且跟阿里爸爸合作掏包二维码
- $alibabaOldMother = new OldMother();
- // 为阿里爸爸加上香笋口味和掏包网二维码
- $alibabaOldMother->setTasteBehavior(new bambooShootsBehavior());
- $alibabaOldMother->setVendorBehavior(new alibabaBehavior());
- var_dump($alibabaOldMother->manufacture()); // 开始生产
- var_dump($alibabaOldMother->doTasteBehavior()); // 加入口味
- var_dump($alibabaOldMother->doVendorBehavior()); // 加入合作企业
测试结果:
- string(32) "制造老干妈:黄豆+辣椒!"
- string(13) "口味:芽菜"
- string(31) "合作企业:腾讯:QQ飞车CDK"
- string(24) "========================"
- string(32) "制造老干妈:黄豆+辣椒!"
- string(13) "口味:香笋"
- string(41) "合作企业:阿里爸爸:掏包二维码"
就这样,生产厂可以给阿里爸爸或疼迅组合任何口味的老干妈,甚至还可以给阿里爸爸贴 QQ 飞车 CDK,或者给疼迅贴掏包网二维码。
这个就是算法族的动态组合!
通过小王的改造,厂里革新了技术,超额完成了指标还获得了表扬!
杰克马也终于获得了老干妈公司的提成。。。。
感觉看故事汇的感觉
你好,看到独角数卡的订单处理模块有您的署名,冒昧想请教下下单的邮件自动发货,是怎么配置的?感激不尽