一、什么是策略模式
要说策略模式,那么我脑袋里有一个例子,是一个策略模式使用的大家,那就是在周星驰的电影《国产凌凌漆》中的----达文西。
达文西:“没错就是我,请叫我文西”。
电影中,角色达文西以其机智和创造力,生动地展示了策略模式的精髓。面对电影中的反派角色金枪客的威胁,达文西,这位“聪明的发明家”,迅速设计并制造了一种应对紧急情况的策略武器——“要你命3000”。
“要你命3000”是一种超级武器,它巧妙地将西瓜刀、铁链、火药、硫酸、毒药、手枪、手榴弹等十多种武器结合在一起,形成了一种看似强大但实际上荒谬的混合武器。达文西在剧中自豪地介绍他的发明:“要你命三千,西瓜刀,铁链,火药,硫酸,毒药,手枪,手榴弹,杀虫剂,每样都能独当一面,现在集中在一起,看你怕不怕?”这句话不仅展现了达文西的幽默感,也体现了策略模式的核心思想:通过组合不同的元素来应对特定的挑战。
这个场景中的“要你命3000”就是策略模式的一个典型应用。策略模式允许我们在运行时根据不同的情况选择不同的算法或行为,就像达文西根据不同的威胁选择不同的武器一样。这种模式使得系统更加灵活,能够适应不断变化的需求。
策略模式的定义: 策略模式是一种行为设计模式,它定义了一系列算法(要你命3000),将每个算法封装起来(要你命3000中的每个武器),并使它们可以互换(可以使用每种武器应对金枪客)。
二、为什么用策略模式
那么我们可以根据这个示例,来讨论一下为什么要使用策略模式(为啥会想到用要你命3000):
-
算法多样性:在电影中,达文西面对不同的敌人和战斗场景,需要不同的武器。这就像在软件开发中,我们可能需要根据不同的输入或条件来选择不同的算法。策略模式允许我们在运行时选择最合适的算法。例子: 假设我们有一个支付系统,需要支持多种支付方式(现金、信用卡、移动支付等)。每种支付方式都是一个策略。通过策略模式,我们可以轻松地添加新的支付方式,而不需要修改现有的代码。
-
代码复用:达文西的“要你命3000”虽然是一个荒谬的发明,但它展示了如何将不同的工具组合起来以应对特定的挑战。在软件开发中,这意味着我们可以重用已有的算法,而不需要每次都重新编写代码。例子: 在图像处理软件中,我们可能有多种图像滤镜(如模糊、锐化、边缘检测等)。每种滤镜都是一个策略。通过策略模式,我们可以在不同的上下文中重用这些滤镜算法,而不需要为每个新功能重新编写代码。
-
简化切换:在电影中,达文西可能需要快速地从一种武器切换到另一种武器。在软件开发中,策略模式允许我们在运行时轻松地切换算法,而不需要复杂的条件判断。例子: 在游戏开发中,我们可能需要根据玩家的选择或游戏状态来切换不同的AI行为。通过策略模式,我们可以轻松地在不同的AI策略之间切换,以适应游戏的不同阶段或玩家的不同行为。
-
提高可读性:达文西的发明虽然荒诞,但它清晰地展示了如何将不同的元素组合起来。在软件开发中,策略模式通过将算法封装在独立的类中,提高了代码的可读性和可理解性。例子: 在文本编辑器中,我们可能有多种文本格式化策略(如加粗、斜体、下划线等)。通过策略模式,我们可以清晰地定义每种格式化策略,使得代码更加模块化和易于理解。
-
易于扩展:达文西的“要你命3000”展示了如何通过组合不同的元素来创造新的发明。在软件开发中,策略模式使得添加新的算法变得非常容易,而不需要修改现有的代码。例子: 在电子商务平台中,我们可能需要支持多种配送策略(如快递、平邮、同城配送等)。通过策略模式,我们可以轻松地添加新的配送策略,而不需要修改现有的订单处理代码。
-
解耦:达文西的发明展示了如何将不同的元素组合起来,而不需要它们之间有直接的依赖关系。在软件开发中,策略模式将算法的实现与使用算法的客户端解耦,提高了系统的灵活性。例子: 在数据库访问层中,我们可能需要支持多种数据库操作(如查询、更新、删除等)。通过策略模式,我们可以将数据库操作的实现与使用这些操作的业务逻辑解耦,使得数据库访问层更加灵活和可维护。
三、策略模式示例
在代码中,我一般很少直接用策略模式,一般直接使用策略枚举:
- 武器策略接口(约束算法的行为):
//武器策略接口 public interface WeaponStrategy {void attack(); }
- 武器的实现类(具体的算法):
//西瓜刀武器策略 public class WatermelonKnifeStrategy implements WeaponStrategy {@Overridepublic void attack() {System.out.println("吃我一西瓜刀!");} } //链条武器策略 public class ChainStrategy implements WeaponStrategy {@Overridepublic void attack() {System.out.println("吃我一链条!");} } //火药武器策略 public class GunpowderStrategy implements WeaponStrategy {@Overridepublic void attack() {System.out.println("吃我一火药!");} }
- 武器组合(要你命3000):
//武器库:枚举 public enum BattleStrategyEnum {WATERMELON_KNIFE("watermelon_knife", new WatermelonKnifeStrategy()),CHAIN("chain", new ChainStrategy()),GUNPOWDER("gunpowder", new GunpowderStrategy());private final String key;private final WeaponStrategy strategy;BattleStrategyEnum(String key, WeaponStrategy strategy) {this.key = key;this.strategy = strategy;}public String getKey() {return key;}public WeaponStrategy getStrategy() {return strategy;}public static WeaponStrategy getStrategyByKey(String key) {for (BattleStrategyEnum strategy : BattleStrategyEnum.values()) {if (strategy.getKey().equals(key)) {return strategy.getStrategy();}}throw new IllegalArgumentException("No strategy found for key: " + key);} }
- 达文西使用场景(根据传入的不同策略,执行对应的策略):
public class BattleContext {private WeaponStrategy strategy;public BattleContext(String strategyKey) {this.strategy = BattleStrategyEnum.getStrategyByKey(strategyKey);}public void executeStrategy() {strategy.use();} }
- 场景(达文西大战金枪客)
public class BattleSimulation {public static void main(String[] args) {// 模拟战斗场景System.out.println("Battle against Golden Gunman starts:");// 创建上下文对象BattleContext context;// 初期战斗策略context = new BattleContext(BattleStrategyEnum.WATERMELON_KNIFE.getKey());System.out.println("吃我一招:");context.executeStrategy();// 中期战斗策略context = new BattleContext(BattleStrategyEnum.CHAIN.getKey());System.out.println("再来一招:");context.executeStrategy();// 最终战斗策略context = new BattleContext(BattleStrategyEnum.GUNPOWDER.getKey());System.out.println("最后一招:");context.executeStrategy();} }