策略模式

把行为抽成接口,用组合注入代替继承——运行时可替换的算法封装。

为什么需要策略模式

想象你在开发一个鸭子游戏。最初只有绿头鸭和红头鸭,它们都能飞、能叫。你写了一个 Duck 父类,把 fly()quack() 放在里面,子类继承即可。一切看起来很美好。

直到产品加了橡皮鸭——它不会飞。你只好在 RubberDuck 里覆盖 fly() 成空方法。然后加了诱饵鸭——它既不会飞也不会叫。再然后加了火箭鸭——它会火箭飞行。你发现每加一种鸭子,就要改父类或覆盖方法,飞行和叫声的行为在继承体系里越搅越乱。

问题的根源是:行为被硬编码在继承体系中。飞行方式和鸭子类型绑死了。改一种行为要波及所有鸭子,新增行为要修改一堆子类。

概念讲解

策略模式的思路是:把行为抽成接口,鸭子用组合持有行为引用

定义了行为的标准接口(如 fly())。每种具体行为是一个独立的实现类——FlyWithWingsFlyNoWayFlyRocketPowered

就是 Duck。它持有 FlyBehaviorQuackBehavior 两个接口引用,performFly() 委托给 flyBehavior.fly()。Duck 不关心具体怎么飞,只管调接口。

可交互
策略模式 · 会飞的鸭子行为抽成接口,Context 用组合持有引用——运行时可替换算法«interface» FlyBehavior+ fly()FlyWithWings用翅膀飞FlyNoWay不会飞FlyRocketPowered火箭推进飞行ModelDuckDuck · ContextflyBehavior: FlyBehaviorperformFly()setFlyBehavior(fb)performFly() → 不会飞performFly() → 火箭飞行!行为是独立对象,运行时可替换——新增飞法加实现类即可,Duck 零改动

第 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 上下文
  • 核心原则:多用组合少用继承——行为用组合注入,不用继承硬编码
  • 适用场景:算法变体多、需运行时切换、需隔离算法变化

讨论

评论区加载中…