《Head First 设计模式》学习笔记
核心框架
设计模式的本质
- “经验复用” 而非 “代码复用” —— 设计模式是解决特定情境下问题的成熟方案
- 核心理念:找出变化之处,封装起来,让系统具备弹性
关键OO设计原则(贯穿全书)
| 原则 | 说明 |
|---|
| 封装变化 | 把会变化的部分取出并独立出来,不要和不需要变化的部分混在一起 |
| 针对接口编程 | 变量的声明类型应该是超类型(接口或抽象类),而不是具体实现类 |
| 组合优于继承 | 多用对象组合(委托行为),少用类继承 |
第1章:设计模式入门 —— 模拟鸭子案例
问题背景
- 开发 SimUDuck 模拟鸭子游戏
- 初始设计:Duck 超类 + 各种鸭子子类(MallardDuck、RedheadDuck、RubberDuck、DecoyDuck)
- 所有鸭子都有 quack()、swim()、display() 方法
引入需求的挑战
- 需要飞的行为 → Joe 在 Duck 中加 fly() → 橡皮鸭和诱饵鸭也会飞(错误!)
- 橡皮鸭不会叫 → 子类覆盖 quack() 为 squeak()
- 诱饵鸭不飞也不叫 → 需要覆盖两个方法为”什么都不做”
使用接口的问题
- 定义 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
- 动态增减:支持运行时注册/注销观察者
与其他知识关联
可行动点