php23种设计模式-策略模式(1)

php23种设计模式-策略模式(1)

简介:本系列将用实际业务场景带你理解设计模式,用简单粗暴的语言讲解结合实际案例来理解实际模式的思想

文章目录

  • php23种设计模式-策略模式(1)
  • 策略模式
    • 官方解释
    • 白话版
    • 例子A
      • 业务场景
      • 业务需求
      • 未来拓展
      • 实现思路
      • 代码实现
  • 策略模式的组成
    • 策略模式的组成部分可分为以下4块:
    • 策略模式的使用原理
    • 如果不使用设计模式会产生那些后果
  • 总结


策略模式

官方解释

定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

白话版

将通用不变的方法分装成父类进行集成,将经常变化的方法搞成接口类并使用接口实现,实际生产中按
设计需求调用接口实现(我这么说你可能不太明白,下面我们用实际例子来进行讲解)

例子A

业务场景

现在要开发一款冒险类游戏,游戏中有4个职业分别为国王、王后、侏儒、骑士。可以使用的武器分别为匕首、弓箭、斧头、宝剑。

业务需求

每种职业一次只能使用1种武器进行战斗,但可以在游戏过程中换武器。

未来拓展

无意看到了产品经理后续的开发安排,未来6个月内可能还要新增多个武器,但具体是什么武器产品经理
还没想好,还有可能新增一个职业“拳击手(可能不使用武器)”这个暂时也没想好。

实现思路

  1. 找出不变的东西放在一起
  2. 找出经常变化的东西放在一起
  3. 为了代码的迭代解决版本兼容问题,代码要求之拓展(新增),不可修改旧代码(以前定义好的)

代码实现

注意:设计模式只是一种思想,是为了解决一些不必要麻烦的方案,他可能在某些场景不是最优解,所以并不是所有的代码都要套用设计模式,如果连写个 echo "Hello World";都要使用设计模式,那一定是中了设计模式的毒了!

  1. 在上述需求中那些是不变的东西?职业,上述4种职业是不变的。

  2. 那些是经常会变动的?上述的4种武器,根据产品开发安排后续要加入多种武器。

  3. 我们将不变的且共同拥有的进行封装成一个父类,将经常需要变化的放在一堆,过会在去处理。

  4. 于是你得到了它。
    php23种设计模式-策略模式(1)_第1张图片

    1. 代码实现


//角色父类
abstract class Role{
    //角色
    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 '骑士角色';
    }
}
$role = new Knight;//实例化骑士类
$role->fight;//骑士角色
这样需要那个角色就直接实例化调用那个角色方法即可,下面说说经常变化的地方
根据开发安排得知未来要新增多种“武器”和一个待定职业“拳击手(可能不使用武器)”
这个时候会有小伙伴有疑问,为什么不把武器方法也封装进抽象父类中,因为
“拳击手”职业可能不使用武器,如果封装进去那么“拳击手”如果真的不使用武器怎么办?要是后期新增
“拳击手”职业后产品经理又想给“拳击手”加上武器怎么办?这会造成代码的不易维护。
  1. 修改代码,加入武器
    php23种设计模式-策略模式(1)_第2张图片


//角色父类
abstract class Role{
    protected $weapon;//武器

    public function setWeapon(WeaponBehavior $weapon){
        $this->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个抽象父类:用来约束公共通用方法接口、设置公共方法、设置公共参数、统一进行调用
  2. 至少1个或多个抽象类实现子类:用来实现抽象父类中的抽象方法
  3. 至少1个接口类:用来约束那些抽象子类中不一定要实现的方法
  4. 至少1个或多个接口实现类:用来实现接口类中的方法

策略模式的使用原理

  1. 实例化抽象类实现子类调用子类中的具体方法,完成基础需求,如上述“设置职业”的需求;
  2. 通过向抽象类传递参数来确定调用具体的拓展类中的实现方法,如上面例子中,侏儒+匕首的 组合。

如果不使用设计模式会产生那些后果

如果没有使用设计模式也许会这样编码



abstract class Role{
    public abstract function fight();
    public function knifeBehavior(){
        echo '实现匕首刺杀';
    }
    public function bowAndArrowBehavior(){
        echo '实现弓箭射击';
    }
    public function axeBehavior(){
        echo '实现斧头劈砍';
    }
    public function swordBehavior(){
        echo '实现宝剑挥舞';
    }
}
//角色子类(王后)
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 '骑士角色';
    }
}

$obj = new Knight;
$obj->fight();
$obj->swordBehavior();
设想一下,某天产品经理说骑士角色使用宝剑的时候不够帅,我想让骑士使用宝剑的时候可以
挥舞两次,你会怎么办?
如果改变父类,那么其他职业使用宝剑时也会收到影响。

要是某天“拳击手”职业真的上线了,但是他只用到了父类中的角色设置方法,其他的武器
他也没用到,那他的继承父类就显得有些多余。

项目被新的产品接手了,产品觉得匕首不好,私自让开发将匕首下架了,此时父类的匕首恰好又
不是抽象方法,那么有职业类一旦切换匕首连代码都跑不起来报错说没有“匕首方法”,想改这个Bug还
不好找,写了那多代码开发也忘记都在哪里调用过“匕首方法”了,只能一个一个找,耗时又费力。

当开发者发现问题了,将父类的的武器方法都设置成抽象方法,这回所有的武器都要在每个
继承他的角色类中进行实现。这显然有点浪费时间啊,这代码都是重复的为啥不复用。

要是某天游戏做大了,新出了20个职业,这是产品说新版本的宝剑来个加成属性吧,这时候发现每个职业的
武器实现方法都是在职业类自身中,并没有放在父类中进行继承,这次每个职业的宝剑加成百分比%又
不同,可能产品眼里看就是传个参数的事没啥难度。方法多接个参数,职业类里面写
的实现方法,20几个职业类中的宝剑方法都要改一遍,想想都是麻烦,要是在把参数传错了还不
是开发的锅!

总结

提示:下文纯属闲扯,当个乐看看就好:
期初我在看Gof23种设计模式的时候也感觉挺扯的,有种脱了裤子放屁的意思,期初也想不明白为啥要搞这设计模式,为啥会有这种感觉,因为我写的业务系统太简单了,简单到设计模式只会让我的业务系统变得更复杂!
设计模式只是一种思想,是一种结局问题的方案,他也行并不是你的最优解,所以不是写什么业务都要套用设计模式的,就比如上面的例子,要是为了长远发展应对业务的多变和不确定我才去使用设计模式。要是当时产品说就4个职业4种武器,那还用啥设计模式啊,武器一个类,职业一个类,现用现new呗,简单粗暴直接解决问题,但是产品就是这样总是在不断变化和更新的,更新的时候既要考虑对旧版本的兼容问题,又要兼顾新版本的功能实现,也许只有当业务足够复杂时才能真正体会到设计模式的好处吧。

你可能感兴趣的:(php-Gof23种设计模式,策略模式,php,后端,设计模式)