目的:
策略模式定义了算法族,分别封装起来,让他们之间可以相互替换。该模式让算法独立于使用它的客户而独立变化。
看到上面文字的同学可能有一点懵,设计模式的概述总是这么深奥和苦涩。
没事,我们来换个说法方便大家理解一下。
老规矩,还是以举例子来说,就拿老干妈吧。(迷之举例老干妈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,或者给疼迅贴掏包网二维码。
这个就是算法族的动态组合!
通过小王的改造,厂里革新了技术,超额完成了指标还获得了表扬!
杰克马也终于获得了老干妈公司的提成。。。。
感觉看故事汇的感觉
你好,看到独角数卡的订单处理模块有您的署名,冒昧想请教下下单的邮件自动发货,是怎么配置的?感激不尽