1Head First Design Patterns(第2版)

策略模式

封装算法

策略模式组合优于继承接口编程行为委托

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第1章:策略模式(Strategy Pattern)

一、导读

1.1 本章学习目标

第1章介绍设计模式中最基础也最实用的模式之一——策略模式。策略模式的核心思想是:定义一系列算法,把它们封装起来,并且使它们可以互相替换。本章通过一个生动的鸭子模拟器的故事,让你直观理解策略模式的精髓。

本章的核心学习目标包括:

  • 理解策略模式的意图:将变化的行为封装为独立的策略类。
  • 掌握"组合优于继承"的原则:理解为什么继承不是解决行为变化的好方法。
  • 学会使用接口编程:面向接口而非实现编程。
  • 理解行为委托:将行为委托给策略对象。
  • 掌握策略模式的适用场景:何时使用策略模式。

1.2 为什么第一章如此重要

策略模式是理解设计模式的起点。它展示了设计模式的核心思想:找出变化点,将变化封装起来。这个思想贯穿所有设计模式。

Head First 系列以独特的教学方式著称——通过故事、对话、谜题来传达知识。本章通过一个鸭子模拟器的故事,让你在轻松的氛围中理解策略模式的设计动机和实现方式。

1.3 核心问题引导

为什么继承不适合解决行为变化的问题?

什么是"组合优于继承"?

策略模式中的三个角色是什么?

如何实现策略的运行时切换?

策略模式有哪些实际应用场景?


二、核心概念详解

1.1 问题:鸭子模拟器的设计挑战

假设你在开发一个鸭子模拟器,最初的需求很简单:

java
// 初始设计
public abstract class Duck {
    public void quack() {
        System.out.println("Quack");
    }
    
    public void swim() {
        System.out.println("All ducks swim");
    }
    
    public abstract void display();
}

public class MallardDuck extends Duck {
    @Override
    public void display() {
        System.out.println("I'm a Mallard Duck");
    }
}

public class RubberDuck extends Duck {
    @Override
    public void quack() {
        System.out.println("Squeak");  // 橡皮鸭不会嘎嘎叫
    }
    
    @Override
    public void display() {
        System.out.println("I'm a Rubber Duck");
    }
}

现在需求变了——需要鸭子能飞。最简单的做法是在基类中添加 fly() 方法:

java
public abstract class Duck {
    public void quack() { System.out.println("Quack"); }
    public void swim() { System.out.println("All ducks swim"); }
    public void fly() { System.out.println("Flying..."); }  // 新增
    public abstract void display();
}

问题来了:橡皮鸭不会飞!但因为它继承了 Duck,所以也会飞了。

1.2 继承方案的困境

为了解决橡皮鸭不能飞的问题,你可能会在子类中覆盖 fly() 方法:

java
public class RubberDuck extends Duck {
    @Override
    public void fly() {
        // 什么都不做——橡皮鸭不会飞
    }
}

但这样做有几个问题:

代码重复:如果还有其他不会飞的鸭子,每个都要覆盖 fly()。

运行时行为不可知:编译时无法知道哪些鸭子会飞。

变化难以追踪:鸭子的行为(飞、叫)经常变化,每次变化都需要修改多个类。

1.3 策略模式的解决方案

策略模式的核心思想是:将变化的行为抽取出来,封装为独立的类。

java
// 定义行为接口
public interface FlyBehavior {
    void fly();
}

public interface QuackBehavior {
    void quack();
}

// 实现具体行为
public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I'm flying with wings!");
    }
}

public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I can't fly.");
    }
}

public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Quack!");
    }
}

public class Squeak implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Squeak!");
    }
}

public class MuteQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("...silence...");
    }
}

// 使用组合代替继承
public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public Duck() {}
    
    public abstract void display();
    
    public void performFly() {
        flyBehavior.fly();  // 委托给行为对象
    }
    
    public void performQuack() {
        quackBehavior.quack();  // 委托给行为对象
    }
    
    public void swim() {
        System.out.println("All ducks swim.");
    }
    
    // 运行时切换行为
    public void setFlyBehavior(FlyBehavior fb) {
        this.flyBehavior = fb;
    }
    
    public void setQuackBehavior(QuackBehavior qb) {
        this.quackBehavior = qb;
    }
}

// 具体鸭子
public class MallardDuck extends Duck {
    public MallardDuck() {
        flyBehavior = new FlyWithWings();
        quackBehavior = new Quack();
    }
    
    @Override
    public void display() {
        System.out.println("I'm a Mallard Duck");
    }
}

public class RubberDuck extends Duck {
    public RubberDuck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Squeak();
    }
    
    @Override
    public void display() {
        System.out.println("I'm a Rubber Duck");
    }
}

public class ModelDuck extends Duck {
    public ModelDuck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Quack();
    }
    
    @Override
    public void display() {
        System.out.println("I'm a Model Duck");
    }
}

1.4 策略模式的结构

策略模式包含三个角色:

策略接口(Strategy):定义所有具体策略的公共接口。

具体策略(Concrete Strategy):实现策略接口的具体算法。

上下文(Context):持有一个策略对象的引用,将请求委托给策略对象。

┌─────────────────┐
│    Context       │
│─────────────────│
│ - strategy      │──────┐
│─────────────────│      │
│ + request()     │      │
└─────────────────┘      │
                         │
┌─────────────────┐      │
│   <<interface>>  │◄─────┘
│    Strategy      │
│─────────────────│
│ + algorithm()   │
└─────────────────┘
        ▲
        │
┌───────┴───────┐
│ ConcreteStratA │  ConcreteStratB
│───────────────│
│ + algorithm() │
└───────────────┘

1.5 运行时动态切换策略

策略模式的一个强大优势是可以在运行时切换行为:

java
public class DuckSimulator {
    public static void main(String[] args) {
        Duck model = new ModelDuck();
        
        model.display();         // I'm a Model Duck
        model.performFly();      // I can't fly.
        
        // 运行时切换飞行策略
        model.setFlyBehavior(new FlyWithWings());
        model.performFly();      // I'm flying with wings!
    }
}

1.6 设计原则

本章体现了两个重要的设计原则:

找出变化点,将它们封装起来:将鸭子的飞行和叫声行为封装为独立的策略类。

组合优于继承:使用组合(持有行为对象的引用)代替继承(在子类中覆盖方法)。


三、实战案例

3.1 支付策略

java
// 策略接口
public interface PaymentStrategy {
    PaymentResult pay(Order order);
}

// 具体策略
public class CreditCardPayment implements PaymentStrategy {
    @Override
    public PaymentResult pay(Order order) {
        // 信用卡支付逻辑
        return new PaymentResult(true, "Paid with credit card");
    }
}

public class AlipayPayment implements PaymentStrategy {
    @Override
    public PaymentResult pay(Order order) {
        // 支付宝支付逻辑
        return new PaymentResult(true, "Paid with Alipay");
    }
}

public class WechatPayPayment implements PaymentStrategy {
    @Override
    public PaymentResult pay(Order order) {
        // 微信支付逻辑
        return new PaymentResult(true, "Paid with WeChat Pay");
    }
}

// 上下文
public class PaymentContext {
    private PaymentStrategy strategy;
    
    public PaymentContext(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    
    public PaymentResult executePayment(Order order) {
        return strategy.pay(order);
    }
}

// 使用
PaymentContext context = new PaymentContext(new CreditCardPayment());
PaymentResult result = context.executePayment(order);

// 运行时切换
context.setStrategy(new AlipayPayment());
result = context.executePayment(order);

3.2 排序策略

java
// 策略接口
public interface SortStrategy {
    <T extends Comparable<T>> List<T> sort(List<T> list);
}

// 具体策略
public class QuickSort implements SortStrategy {
    @Override
    public <T extends Comparable<T>> List<T> sort(List<T> list) {
        // 快速排序实现
        List<T> result = new ArrayList<>(list);
        quickSort(result, 0, result.size() - 1);
        return result;
    }
    
    private <T extends Comparable<T>> void quickSort(List<T> list, int low, int high) {
        if (low < high) {
            int pivot = partition(list, low, high);
            quickSort(list, low, pivot - 1);
            quickSort(list, pivot + 1, high);
        }
    }
    
    private <T extends Comparable<T>> int partition(List<T> list, int low, int high) {
        T pivot = list.get(high);
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (list.get(j).compareTo(pivot) <= 0) {
                i++;
                Collections.swap(list, i, j);
            }
        }
        Collections.swap(list, i + 1, high);
        return i + 1;
    }
}

public class MergeSort implements SortStrategy {
    @Override
    public <T extends Comparable<T>> List<T> sort(List<T> list) {
        // 归并排序实现
        if (list.size() <= 1) return list;
        int mid = list.size() / 2;
        List<T> left = sort(list.subList(0, mid));
        List<T> right = sort(list.subList(mid, list.size()));
        return merge(left, right);
    }
    
    private <T extends Comparable<T>> List<T> merge(List<T> left, List<T> right) {
        List<T> result = new ArrayList<>();
        int i = 0, j = 0;
        while (i < left.size() && j < right.size()) {
            if (left.get(i).compareTo(right.get(j)) <= 0) {
                result.add(left.get(i++));
            } else {
                result.add(right.get(j++));
            }
        }
        while (i < left.size()) result.add(left.get(i++));
        while (j < right.size()) result.add(right.get(j++));
        return result;
    }
}

// 上下文
public class Sorter {
    private SortStrategy strategy;
    
    public Sorter(SortStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }
    
    public <T extends Comparable<T>> List<T> sort(List<T> list) {
        return strategy.sort(list);
    }
}

// 使用
Sorter sorter = new Sorter(new QuickSort());
List<Integer> sorted = sorter.sort(List.of(5, 2, 8, 1, 9, 3));

// 对于小数据集,切换到插入排序
sorter.setStrategy(new InsertionSort());
sorted = sorter.sort(List.of(5, 2, 8));

四、本章小结

第1章通过鸭子模拟器的故事,讲解了策略模式的核心知识:

策略模式的意图:定义一系列算法,把它们封装起来,使它们可以互相替换。

组合优于继承:使用组合代替继承来应对行为变化。

面向接口编程:策略接口定义了所有策略的公共行为。

运行时切换:可以在运行时动态更换策略。

设计原则:找出变化点,封装变化。

策略模式是最简单也最实用的设计模式之一。当你发现代码中有大量的 if-else 或 switch-case 来选择不同的行为时,考虑使用策略模式。


五、思考与练习

思考题

策略模式和 if-else 相比有什么优势?

策略模式会增加类的数量,这是否是一个问题?

策略模式和状态模式有什么区别?

在什么场景下应该使用策略模式?

策略模式如何与工厂模式配合使用?

编程练习

实现导航策略:设计一个导航系统,支持多种路线规划策略(最短路径、最快路径、避开高速)。

实现压缩策略:设计一个文件压缩工具,支持多种压缩算法(ZIP、GZIP、7Z)。

实现验证策略:设计一个表单验证系统,支持多种验证规则(邮箱、手机号、密码强度)。


1/12策略模式
0%