简介:本系列将用实际业务场景带你理解设计模式,用简单粗暴的语言讲解结合实际案例来理解实际模式的思想
文章目录- php23种设计模式-策略模式(1)
- 策略模式
- 官方解释
- 白话版
- 例子A
- 业务场景
- 业务需求
- 未来拓展
- 实现思路
- 代码实现
- 策略模式的组成
- 策略模式的组成部分可分为以下4块:
- 策略模式的使用原理
- 如果不使用设计模式会产生那些后果
- 总结
策略模式 官方解释
定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。白话版
将通用不变的方法分装成父类进行集成,将经常变化的方法搞成接口类并使用接口实现,实际生产中按 设计需求调用接口实现(我这么说你可能不太明白,下面我们用实际例子来进行讲解)
例子A 业务场景
现在要开发一款冒险类游戏,游戏中有4个职业分别为国王、王后、侏儒、骑士。可以使用的武器分别为匕首、弓箭、斧头、宝剑。业务需求
每种职业一次只能使用1种武器进行战斗,但可以在游戏过程中换武器。未来拓展
无意看到了产品经理后续的开发安排,未来6个月内可能还要新增多个武器,但具体是什么武器产品经理 还没想好,还有可能新增一个职业“拳击手(可能不使用武器)”这个暂时也没想好。实现思路
- 找出不变的东西放在一起
- 找出经常变化的东西放在一起
- 为了代码的迭代解决版本兼容问题,代码要求之拓展(新增),不可修改旧代码(以前定义好的)
注意:设计模式只是一种思想,是为了解决一些不必要麻烦的方案,他可能在某些场景不是最优解,所以并不是所有的代码都要套用设计模式,如果连写个 echo "Hello World";都要使用设计模式,那一定是中了设计模式的毒了!
-
在上述需求中那些是不变的东西?职业,上述4种职业是不变的。
-
那些是经常会变动的?上述的4种武器,根据产品开发安排后续要加入多种武器。
-
我们将不变的且共同拥有的进行封装成一个父类,将经常需要变化的放在一堆,过会在去处理。
-
于是你得到了它。
- 代码实现
fight;//骑士角色
这样需要那个角色就直接实例化调用那个角色方法即可,下面说说经常变化的地方 根据开发安排得知未来要新增多种“武器”和一个待定职业“拳击手(可能不使用武器)” 这个时候会有小伙伴有疑问,为什么不把武器方法也封装进抽象父类中,因为 “拳击手”职业可能不使用武器,如果封装进去那么“拳击手”如果真的不使用武器怎么办?要是后期新增 “拳击手”职业后产品经理又想给“拳击手”加上武器怎么办?这会造成代码的不易维护。
- 修改代码,加入武器
weapon = $weapon; } public function getWeapon(){ return $this->weapon->setWeapon(); } //角色 public abstract function fight(); } //角色子类(王后) class Queen extends Role { public function fight() { echo '王后角色'; } } //角色子类(国王) class King extends Role { public function fight() { echo '国王角色'; } } //角色子类(侏儒) class Troll extends Role { public function fight() { echo '侏儒角色'; } } //角色子类(骑士) class Knight extends Role { public function fight() { echo '骑士角色'; } } //-------------分割线-------------- //武器接口类 interface WeaponBehavior{ public function setWeapon(); } //武器-匕首 class KnifeBehavior implements WeaponBehavior { public function setWeapon(){ echo '实现匕首刺杀'; } } //武器-弓箭 class BowAndArrowBehavior implements WeaponBehavior { public function setWeapon(){ echo '实现弓箭射击'; } } //武器-斧头 class AxeBehavior implements WeaponBehavior { public function setWeapon(){ echo '实现斧头劈砍'; } } //武器-宝剑 class SwordBehavior implements WeaponBehavior { public function setWeapon(){ echo '实现宝剑挥舞'; } } //侏儒使用匕首 $troll = new Troll; $troll->fight();//确定角色 //给角色设置武器 $weapon = new KnifeBehavior;//实例化武器-匕首 $obj = $troll->setWeapon($weapon);//设置武器 $troll->getWeapon();//获取武器
我们新增了1个武器接口和4个武器实现类,有小伙伴可能会想为啥要是用接口类对武器实现类进行约束? 为什么不直接创建4个武器类? 答:目的是要约束武器的实现方法,如果直接创建4个武器的实现类要是实现方法的名字、参数不一致怎么办? 多人协作开发的时候每人负责1个武器类的实现,到最后可能会出现好多个不同的实现方法名称,出接口 的时候要出好少,而且参数不经约束的话,当拥有了很多武器的时候扔给你去维护你会发现那简直就是噩梦。 所以要使用接口父类进行接口实现类的方法名参数进行约束,这样接口是统一的,维护起来也会更加方便,而且 用了接口实现后接口定义好的方法在实现类中就不能被随意删除,这满足了设计模式的“不可修改只可拓展的原则”策略模式的组成 策略模式的组成部分可分为以下4块:
- 至少1个抽象父类:用来约束公共通用方法接口、设置公共方法、设置公共参数、统一进行调用
- 至少1个或多个抽象类实现子类:用来实现抽象父类中的抽象方法
- 至少1个接口类:用来约束那些抽象子类中不一定要实现的方法
- 至少1个或多个接口实现类:用来实现接口类中的方法
- 实例化抽象类实现子类调用子类中的具体方法,完成基础需求,如上述“设置职业”的需求;
- 通过向抽象类传递参数来确定调用具体的拓展类中的实现方法,如上面例子中,侏儒+匕首的 组合。
如果没有使用设计模式也许会这样编码
fight(); $obj->swordBehavior();
设想一下,某天产品经理说骑士角色使用宝剑的时候不够帅,我想让骑士使用宝剑的时候可以 挥舞两次,你会怎么办? 如果改变父类,那么其他职业使用宝剑时也会收到影响。 要是某天“拳击手”职业真的上线了,但是他只用到了父类中的角色设置方法,其他的武器 他也没用到,那他的继承父类就显得有些多余。 项目被新的产品接手了,产品觉得匕首不好,私自让开发将匕首下架了,此时父类的匕首恰好又 不是抽象方法,那么有职业类一旦切换匕首连代码都跑不起来报错说没有“匕首方法”,想改这个Bug还 不好找,写了那多代码开发也忘记都在哪里调用过“匕首方法”了,只能一个一个找,耗时又费力。 当开发者发现问题了,将父类的的武器方法都设置成抽象方法,这回所有的武器都要在每个 继承他的角色类中进行实现。这显然有点浪费时间啊,这代码都是重复的为啥不复用。 要是某天游戏做大了,新出了20个职业,这是产品说新版本的宝剑来个加成属性吧,这时候发现每个职业的 武器实现方法都是在职业类自身中,并没有放在父类中进行继承,这次每个职业的宝剑加成百分比%又 不同,可能产品眼里看就是传个参数的事没啥难度。方法多接个参数,职业类里面写 的实现方法,20几个职业类中的宝剑方法都要改一遍,想想都是麻烦,要是在把参数传错了还不 是开发的锅!总结
提示:下文纯属闲扯,当个乐看看就好:
期初我在看Gof23种设计模式的时候也感觉挺扯的,有种脱了裤子放屁的意思,期初也想不明白为啥要搞这设计模式,为啥会有这种感觉,因为我写的业务系统太简单了,简单到设计模式只会让我的业务系统变得更复杂!
设计模式只是一种思想,是一种结局问题的方案,他也行并不是你的最优解,所以不是写什么业务都要套用设计模式的,就比如上面的例子,要是为了长远发展应对业务的多变和不确定我才去使用设计模式。要是当时产品说就4个职业4种武器,那还用啥设计模式啊,武器一个类,职业一个类,现用现new呗,简单粗暴直接解决问题,但是产品就是这样总是在不断变化和更新的,更新的时候既要考虑对旧版本的兼容问题,又要兼顾新版本的功能实现,也许只有当业务足够复杂时才能真正体会到设计模式的好处吧。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)