策略模式把行为抽成接口,用组合注入代替继承——运行时可替换的算法封装。为什么需要策略模式 想象你在开发一个鸭子游戏。最初只有绿头鸭和红头鸭,它们都能飞、能叫。你写了一个 Duck 父类,把 fly() 和 quack() 放在里面,子类继承即可。一切看起来很美好。 直到产品加了橡皮鸭——它不会飞。你只好在 RubberDuck 里覆盖 fly() 成空方法。然后加了诱饵鸭——它既不会飞也不会叫。再然后加了火箭鸭——它会火箭飞行。你发现每加一种鸭子,就要改父类或覆盖方法,飞行和叫声的行为在继承体系里越搅越乱。 问题的根源是:行为被硬编码在继承体系中。飞行方式和鸭子类型绑死了。改一种行为要波及所有鸭子,新增行为要修改一堆子类。 概念讲解 策略模式的思路是:把行为抽成接口,鸭子用组合持有行为引用。 定义了行为的标准接口(如 fly())。每种具体行为是一个独立的实现类——FlyWithWings、FlyNoWay、FlyRocketPowered。 就是 Duck。它持有 FlyBehavior 和 QuackBehavior 两个接口引用,performFly() 委托给 flyBehavior.fly()。Duck 不关心具体怎么飞,只管调接口。 ⚡可交互策略模式 · 会飞的鸭子行为抽成接口,Context 用组合持有引用——运行时可替换算法«interface» FlyBehavior+ fly()FlyWithWings用翅膀飞FlyNoWay不会飞FlyRocketPowered火箭推进飞行ModelDuckDuck · ContextflyBehavior: FlyBehaviorperformFly()setFlyBehavior(fb)performFly() → 不会飞performFly() → 火箭飞行!行为是独立对象,运行时可替换——新增飞法加实现类即可,Duck 零改动12345⏮ 上一步▶ 播放下一步 ⏭第 1 / 5 步 · 把飞行行为抽成 FlyBehavior 接口,三个具体策略各自封装一种飞法从「继承获得行为」到「组合注入行为」:Duck 不再继承飞行,而是持有飞行行为对象,用 setter 在运行时替换。策略模式定义算法族,把每个算法分别封装起来,让它们可以互相替换。 Context 只依赖策略接口,算法的变化独立于使用它的客户端—— 继承把行为写死在编译期,组合让行为活到运行时。 关键转变:从「继承获得行为」到「组合注入行为」。Duck 不再继承飞行行为,而是持有一个飞行行为对象。setter 方法让行为可以在运行时替换——一只鸭子可以从「不会飞」变成「火箭飞行」。 代码对照 // 1. 策略接口 public interface FlyBehavior { void fly(); } // 2. 具体策略 public class FlyWithWings implements FlyBehavior { public void fly() { System.out.println("用翅膀飞"); } } public class FlyRocketPowered implements FlyBehavior { public void fly() { System.out.println("火箭推进飞行"); } } // 3. Context——用组合持有策略 public abstract class Duck { protected FlyBehavior flyBehavior; public void setFlyBehavior(FlyBehavior fb) { this.flyBehavior = fb; } public void performFly() { flyBehavior.fly(); } } // 4. 使用——运行时切换行为 Duck model = new ModelDuck(); model.performFly(); // 最初不会飞 model.setFlyBehavior(new FlyRocketPowered()); model.performFly(); // 火箭飞行! Duck 把 fly() 委托给 flyBehavior 对象。新增飞行方式只需加实现类,Duck 一行不改——符合「多用组合少用继承」。 常见误区 小结与练习 策略模式把行为封装成接口,Context 用组合持有策略引用,运行时可替换 三要素:Strategy 接口、ConcreteStrategy 具体策略、Context 上下文 核心原则:多用组合少用继承——行为用组合注入,不用继承硬编码 适用场景:算法变体多、需运行时切换、需隔离算法变化 ← 上一章学习地图下一章 →观察者模式讨论评论区加载中…