《Head First 设计模式》学习笔记

核心框架

设计模式的本质

  • “经验复用” 而非 “代码复用” —— 设计模式是解决特定情境下问题的成熟方案
  • 核心理念:找出变化之处,封装起来,让系统具备弹性

关键OO设计原则(贯穿全书)

原则说明
封装变化把会变化的部分取出并独立出来,不要和不需要变化的部分混在一起
针对接口编程变量的声明类型应该是超类型(接口或抽象类),而不是具体实现类
组合优于继承多用对象组合(委托行为),少用类继承

第1章:设计模式入门 —— 模拟鸭子案例

问题背景

  • 开发 SimUDuck 模拟鸭子游戏
  • 初始设计:Duck 超类 + 各种鸭子子类(MallardDuck、RedheadDuck、RubberDuck、DecoyDuck)
  • 所有鸭子都有 quack()、swim()、display() 方法

引入需求的挑战

  1. 需要飞的行为 → Joe 在 Duck 中加 fly() → 橡皮鸭和诱饵鸭也会飞(错误!)
  2. 橡皮鸭不会叫 → 子类覆盖 quack() 为 squeak()
  3. 诱饵鸭不飞也不叫 → 需要覆盖两个方法为”什么都不做”

使用接口的问题

  • 定义 Flyable、Quackable 接口
  • 只有会飞的鸭子才实现 Flyable
  • 问题:不同鸭子的飞行行为可能有差异,但接口没有实现代码 → 代码重复

解决方案:行为类(策略模式的前奏)

  • 把 fly() 和 quack() 从 Duck 类中抽离
  • 创建行为接口:FlyBehavior 和 QuackBehavior
  • 每个行为有多个具体实现类:
    • FlyBehavior: FlyWithWings, FlyNoWay
    • QuackBehavior: Quack, Squeak, MuteQuack
  • Duck 类持有行为接口引用,通过 setter 在运行时动态设置
// Duck 类核心结构
public class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public void performFly() {
        flyBehavior.fly();  // 委托给行为对象
    }
    
    public void performQuack() {
        quackBehavior.quack();  // 委托给行为对象
    }
    
    // 运行时可动态改变行为
    public void setFlyBehavior(FlyBehavior fb) { ... }
    public void setQuackBehavior(QuackBehavior qb) { ... }
}

关键收获

  • 继承的陷阱:子类共享父类行为,但某些行为不适合所有子类
  • 组合的力量:通过组合行为对象,可以灵活配置每个实例的行为
  • 运行时改变:通过 setter 方法,可以在运行时动态切换行为

第2章:观察者模式(Observer Pattern)

应用场景:Weather-O-Rama 气象站

  • 需要实时显示三个布告板:
    • 当前状况:温度、湿度、气压
    • 气象统计:平均/最高/最低温度
    • 天气预报:基于数据预测
  • 要求:数据更新时,所有布告板自动刷新;支持动态增删布告板

错误示范(紧耦合)

// WeatherData 直接依赖具体布告板类
public void measurementsChanged() {
    float temp = getTemperature();
    float humidity = getHumidity();
    float pressure = getPressure();
    currentConditionsDisplay.update(temp, humidity, pressure);
    statisticsDisplay.update(temp, humidity, pressure);
    forecastDisplay.update(temp, humidity, pressure);
}

问题:新增布告板需要修改 WeatherData 代码 → 违反开闭原则

观察者模式结构

Subject (主题)           Observer (观察者)
┌──────────────┐        ┌──────────────┐
│   Weather-   │──notify──→│   Display    │
│   Data       │        │   Observer   │
│              │        │   (接口)     │
│ - register   │        │   update()   │
│ - remove     │        │              │
│ - notify     │        └──────────────┘
└──────────────┘

核心概念

角色说明
Subject(主题)维护观察者列表,提供注册/删除/通知方法
Observer(观察者)定义更新接口,主题状态变化时自动收到通知
ConcreteSubject持有具体状态,状态变化时通知所有观察者
ConcreteObserver实现更新逻辑,从主题获取最新数据

报纸订阅类比

  • 出版商 = Subject:维护订阅者清单,有新内容时通知所有订阅者
  • 订阅者 = Observer:订阅后自动收到新内容,可随时取消
  • 一对多关系:一个主题可有多个观察者

关键收获

  • 松耦合:主题不依赖具体观察者,只依赖 Observer 接口
  • 可扩展:新增布告板只需实现 Observer 接口,无需修改 WeatherData
  • 动态增减:支持运行时注册/注销观察者

与其他知识关联

可行动点

  • 在实际项目中寻找可以用”行为类”替换继承的地方
  • 检查现有代码中是否有紧耦合的观察者场景
  • 理解 Java 中 Observable 类和 Observer 接口的标准实现