策略模式是一种行为型设计模式, 它的核心思想是: 定义一系列算法, 把它们一个个封装起来, 并且使它们可以互相替换. 这样, 客户端就可以根据不通的需求, 在运行时选择不同的算法, 而无需修改客户端自身的代码.
生活中的例子: 出行方式选择
想象一下你周末要去一个公园玩. 从你家到公园有多种出行方式(策略):
- 步行: 健康\环保, 但速度慢, 适合短距离
- 骑自行车: 比步行块, 也能锻炼身体.
- 开车: 最快最舒适, 但需要花钱加油和找停车位.
- 乘坐公共交通: 便宜, 但可能需要换成, 花费时间不确定.
这里的”你”就是客户端(Context).
这里的”出行方式”就是策略(Strategy).
你可以根据当前的天气\你的心情\预算以及距离来动态选择最合适的出行方式. 你不需要僚机额每种出行方式的具体细节(比如汽车发动机原理\公交车路线规划), 你只需要知道如何使用它们(比如开车需要钥匙,坐公交需要投币).
如果有一天, 出现了一种新的出行方式, 比如”坐火箭”, 你可以很容易地将其作为一种新的策略加入你的选择列表, 而你本人(客户端)不需要做任何改变.
Unity中的例子: 角色攻击系统
假设我们正在开发一个动作游戏, 我们的主角可以使用多种武器进行攻击.
- 如果不使用策略模式: 我们可能会在主角的脚本里写一大堆
if-else或者swith-case语句来判断当前使用的是那种武器, 然后执行对应的攻击逻辑.
这种方式的问题:
- 代码臃肿: 所有攻击逻辑都挤在一个方法里, 难以阅读和维护.
- 违反开放/封闭原则: 当我们想添加一种新的武器(比如Axe), 我们必须修改
PlayerAttack脚本的Attack方法, 增加新的else if分支. 这会带来潜在的风险, 可能破坏已有的功能.
- 耦合度高: 主角的攻击行为与具体的武器实现紧密耦合在一起
使用策略模式来重构
现在, 我们用策略模式来优雅地解决这个问题.
步骤1: 定义策略接口(Strategy Interface)
我们首先定义一个所有攻击策略都必须遵守的”契约”. 这个契约就是一个接口, 它声明了一个所有武器都必须实现的攻击方法.
步骤2: 具体实现策略(Concrete Strategies)
接下来, 我们为每一种武器创建一个类, 让他们去实现
IAttackStrategy 接口. 每个类都封装了自己独特的攻击逻辑.步骤3: 创建上下文(Context)
上下文是策略模式的使用者. 在我们的例子中, 主角(Player)就是上下文. 它会持有一个当前使用的策略对象的引用, 并在需要时调用其
Attack方法步骤4: 在Unity中使用
- 创建一个Player游戏对象, 并将
PlayerController脚本挂载上去.
- 创建UI按钮, 分别用于切换到剑\弓\火球.
- 将这些按钮的
OnClick事件绑定到PlayerController的SetAttackStrategy方法上。 - 对于 “切换到剑” 按钮,你需要在 Inspector 中手动创建一个
SwordAttack的实例并赋值给SetAttackStrategy方法的参数。 - 同理,为其他按钮赋值
BowAttack和FireballAttack的实例。
- 创建一个 “攻击” 按钮, 将其
OnClick事件绑定到PlayerController的OnAttackButtonPressed方法.
现在, 当你运行游戏并点击不同的切换按钮, 然后点击攻击按钮, 你会看到主角会执行不同的攻击动作, 而
PlayerController 脚本本身没有任何修改.策略模式的优点
- 代码清晰, 易于维护: 每种算法(攻击方式)都被封装在独立的类中, 使得代码结构清晰, 易于理解和维护.
- 高扩展性: 当需要添加新的算法(比如斧头攻击)时, 你只需要创建要给新的类去实现
IAttackStrategy接口, 然后再客户端(PlayerController)中通过SetAttackStrategy方法进行切换即可. 无需修改任何现有代码, 完美符合开放/封闭原则.
- 降低耦合度: 客户端(
PlayerController)与具体的算法实现解耦. 客户端只知道IAttackStrategy接口, 不知道具体的SwordAttack或FireballAttack.
- 策略可以动态切换: 正如我们再例子中看到的, 在游戏运行时, 可以根据玩家的输入\游戏状态或其他条件, 随时切换不同的策略.
在Unity中的其他应用场景
- 敌人AI行为: 一个敌人可以有多种行为策略, 如
PatrolStrategy(巡逻)\ChaseStrategy(追击)\AttackStrategy(攻击)\FleeStrategy(逃跑). 根据与玩家的距离或自身血量, 敌人可以动态切换这些策略.
- 路径查找算法: 可以为不同的场景或单位提供不同的寻路策略, 如
AStarStrategy\DijkstraStrategy或者一个简单的FollowThePathStrategy.
- 特效播放策略: 不同的技能或状态可以有不同的特效播放逻辑, 比如有的特效需要跟随目标, 有的需要在原地爆炸, 有的需要播放完后销毁.
- 数据保存 / 加载策略: 可以选择将游戏数据保存到
PlayerPrefs\Json文件或SQLite数据库, 通过策略模式可以轻松切换.
总而言之, 策略模式是一个非常强大且常用的设计模式, 尤其在需要灵活切换算法或行为的游戏开发中. 它能让你的代码更加模块化\可扩展和易于测试.