策略模式是一种行为型设计模式, 它的核心思想是: 定义一系列算法, 把它们一个个封装起来, 并且使它们可以互相替换. 这样, 客户端就可以根据不通的需求, 在运行时选择不同的算法, 而无需修改客户端自身的代码.

生活中的例子: 出行方式选择

想象一下你周末要去一个公园玩. 从你家到公园有多种出行方式(策略):
  1. 步行: 健康\环保, 但速度慢, 适合短距离
  1. 骑自行车: 比步行块, 也能锻炼身体.
  1. 开车: 最快最舒适, 但需要花钱加油和找停车位.
  1. 乘坐公共交通: 便宜, 但可能需要换成, 花费时间不确定.
 
这里的””就是客户端(Context).
这里的”出行方式”就是策略(Strategy).
 
你可以根据当前的天气\你的心情\预算以及距离来动态选择最合适的出行方式. 你不需要僚机额每种出行方式的具体细节(比如汽车发动机原理\公交车路线规划), 你只需要知道如何使用它们(比如开车需要钥匙,坐公交需要投币).
 
如果有一天, 出现了一种新的出行方式, 比如”坐火箭”, 你可以很容易地将其作为一种新的策略加入你的选择列表, 而你本人(客户端)不需要做任何改变.

Unity中的例子: 角色攻击系统

假设我们正在开发一个动作游戏, 我们的主角可以使用多种武器进行攻击.
  • 如果不使用策略模式: 我们可能会在主角的脚本里写一大堆 if-else 或者 swith-case 语句来判断当前使用的是那种武器, 然后执行对应的攻击逻辑.
这种方式的问题:
  1. 代码臃肿: 所有攻击逻辑都挤在一个方法里, 难以阅读和维护.
  1. 违反开放/封闭原则: 当我们想添加一种新的武器(比如Axe), 我们必须修改 PlayerAttack 脚本的 Attack 方法, 增加新的 else if 分支. 这会带来潜在的风险, 可能破坏已有的功能.
  1. 耦合度高: 主角的攻击行为与具体的武器实现紧密耦合在一起

使用策略模式来重构

现在, 我们用策略模式来优雅地解决这个问题.
 
步骤1: 定义策略接口(Strategy Interface)
我们首先定义一个所有攻击策略都必须遵守的”契约”. 这个契约就是一个接口, 它声明了一个所有武器都必须实现的攻击方法.
 
步骤2: 具体实现策略(Concrete Strategies)
接下来, 我们为每一种武器创建一个类, 让他们去实现 IAttackStrategy 接口. 每个类都封装了自己独特的攻击逻辑.
 
步骤3: 创建上下文(Context)
上下文是策略模式的使用者. 在我们的例子中, 主角(Player)就是上下文. 它会持有一个当前使用的策略对象的引用, 并在需要时调用其Attack方法
 
步骤4: 在Unity中使用
  1. 创建一个Player游戏对象, 并将PlayerController 脚本挂载上去.
  1. 创建UI按钮, 分别用于切换到剑\弓\火球.
  1. 将这些按钮的 OnClick 事件绑定到 PlayerController 的 SetAttackStrategy 方法上。
      • 对于 “切换到剑” 按钮,你需要在 Inspector 中手动创建一个 SwordAttack 的实例并赋值给 SetAttackStrategy 方法的参数。
      • 同理,为其他按钮赋值 BowAttack 和 FireballAttack 的实例。
  1. 创建一个 “攻击” 按钮, 将其 OnClick 事件绑定到 PlayerControllerOnAttackButtonPressed 方法.
 
现在, 当你运行游戏并点击不同的切换按钮, 然后点击攻击按钮, 你会看到主角会执行不同的攻击动作, 而PlayerController 脚本本身没有任何修改.

策略模式的优点

  1. 代码清晰, 易于维护: 每种算法(攻击方式)都被封装在独立的类中, 使得代码结构清晰, 易于理解和维护.
  1. 高扩展性: 当需要添加新的算法(比如斧头攻击)时, 你只需要创建要给新的类去实现 IAttackStrategy 接口, 然后再客户端(PlayerController )中通过 SetAttackStrategy 方法进行切换即可. 无需修改任何现有代码, 完美符合开放/封闭原则.
  1. 降低耦合度: 客户端(PlayerController)与具体的算法实现解耦. 客户端只知道IAttackStrategy 接口, 不知道具体的 SwordAttackFireballAttack .
  1. 策略可以动态切换: 正如我们再例子中看到的, 在游戏运行时, 可以根据玩家的输入\游戏状态或其他条件, 随时切换不同的策略.
 

在Unity中的其他应用场景

  • 敌人AI行为: 一个敌人可以有多种行为策略, 如 PatrolStrategy (巡逻)\ ChaseStrategy (追击)\ AttackStrategy (攻击)\ FleeStrategy (逃跑). 根据与玩家的距离或自身血量, 敌人可以动态切换这些策略.
  • 路径查找算法: 可以为不同的场景或单位提供不同的寻路策略, 如AStarStrategy \ DijkstraStrategy 或者一个简单的 FollowThePathStrategy .
  • 特效播放策略: 不同的技能或状态可以有不同的特效播放逻辑, 比如有的特效需要跟随目标, 有的需要在原地爆炸, 有的需要播放完后销毁.
  • 数据保存 / 加载策略: 可以选择将游戏数据保存到 PlayerPrefs\Json文件或SQLite数据库 , 通过策略模式可以轻松切换.
 
总而言之, 策略模式是一个非常强大且常用的设计模式, 尤其在需要灵活切换算法或行为的游戏开发中. 它能让你的代码更加模块化\可扩展和易于测试.