策略模式
一个开发小例子
想象一下, 假如有一天, 你在开发游戏, 需要设计敌人类Enemy
作为父类, 同时下面有子类野猪Boar
和鸟Bird
, 每个敌人都有私有变量attackDamage
用于记录造成的伤害值, 有一个公开方法Attack()
用于造成伤害
classDiagram
class Enemy{
- attackDamage
+ Attack()
}
Enemy <|-- Boar
class Boar{
- attackDamage
+ Attack()
}
Enemy <|-- Bird
class Bird{
- attackDamage
+ Attack()
}
但是后来加了需求, 做到某一关时候新加入了一种敌人喷火龙Dragon
, 它在攻击玩家的时候不仅会有attackDamage
的固定伤害, 玩家身上会起火, 导致每秒会有一个燃烧伤害burningDamage
, 所以又写了一个方法叫做Burning()
用于提供燃烧伤害, 在Attack()
内调用燃烧方法, 现在类图变成了下面这样
classDiagram
class Enemy{
- attackDamage
+ Attack()
}
Enemy <|-- Boar
class Boar{
- attackDamage
+ Attack()
}
Enemy <|-- Bird
class Bird{
- attackDamage
+ Attack()
}
Enemy <|-- Dragon
class Dragon{
- attackDamage
- burningDamage
+ Attack()
+ Burning()
}
到此为止还算正常, 喷火龙这个敌人的特殊性只在它自己的Attack()
和Burning()
中体现
但是后来又增加了需求, 又增加了一个可以喷火的敌人, 巫师, 他的攻击也可以产生一个固定伤害attackDamage
和每秒burningDamage
的燃烧伤害, 此时应该怎么办, 那么自然而然会产生下面两种想法
- 可以把Dragon里的逻辑复制一份到巫师的类中, 但是这样做很不优雅, 要是将来有20个会喷火的敌人的话, 这样的逻辑都得复制一遍, 并且如果燃烧的逻辑做出了修改, 那就得一个代码接一个的修改, 很是麻烦
- 把这部分攻击的逻辑提取出来, 制作一个
Attack
接口, 再写两个类实现接口常规攻击NormalAttack
和燃烧攻击BurningAttack
, 每个Enemy中增加一个成员变量Attack
, 并给它赋值一个具体的实例, 这样具体的攻击逻辑就被分离了出去, 而且需求改变, 要做出修改的话也比较方便
现在类图变成了这样
classDiagram
class Enemy{
- attack
+ SetAttack(Attack attack)
+ Attack()
}
Enemy <|-- Boar
class Boar{
- attack
+ SetAttack(Attack attack)
+ Attack()
}
Enemy <|-- Bird
class Bird{
- attack
+ SetAttack(Attack attack)
+ Attack()
}
Enemy <|-- Dragon
class Dragon{
- attack
+ SetAttack(Attack attack)
+ Attack()
}
Enemy <|-- Wizard
class Wizard{
- attack
+ SetAttack(Attack attack)
+ Attack()
}
class Attack{
- attackDamage
+ Attack()
}
Attack <|-- NormalAttack
class NormalAttack{
- attackDamage
+ Attack()
}
Attack <|-- BurningAttack
class BurningAttack{
- attackDamage
- burningDamage
+ Attack()
+ Burning()
}
现在不同的敌人只需要设置好自己的attack
成员变量就可以实现不同的攻击逻辑
总结: 什么是策略模式
策略模式的核心思想是将不同的算法或行为封装到独立的类中, 然后通过一个通用接口来调用它们。
策略模式定义了一系列算法或策略, 并将每个算法封装在独立的类中, 使得它们可以互相替换。通过使用策略模式, 可以在运行时根据需要选择不同的算法, 而不需要修改代码
什么叫在运行时候选择不同的算法?
想象有一个游戏角色, 他可以使用不同的武器攻击敌人, 比如剑、弓箭和魔法。不同的武器有不同的攻击方式, 比如近战攻击、远程攻击、和范围攻击。策略模式允许在战斗中随时更换武器, 而不需要改变角色本身的代码
如果有新的武器和攻击方式, 那么不需要修改角色类中的代码, 而是新建立一个类实现Weapon接口, 在角色类中只需要修改成员变量weapon即可
下面是类图展示
(注意弓箭造成的伤害在Unity中一般把碰撞器组件加在射出去的箭上Arrow
, 所以下面类图中造成伤害的武器写成了Arrow
而非Bow
弓)
classDiagram
class Character{
- weapon
+ SetWeapon(Weapon weapon)
+ Attack()
}
class Weapon{
+ takeDamage()
}
Weapon <|-- Sword
class Sword{
+ takeDamage()
}
Weapon <|-- Arrow
class Arrow{
+ takeDamage()
}
Weapon <|-- Magic
class Magic{
+ takeDamage()
}
想要更换攻击方式时, 只需要更换策略, 而不需要修改角色的代码逻辑。策略模式的好处是使得算法的选择与算法的实现分离, 提高了代码的灵活性和可维护性