设计模式
设计模式是针对特定问题的可复用的优秀解决方案,是长期软件开发实践的经验总结。
一、设计模式概述
1.1 什么是设计模式
设计模式描述了在特定情境下,类和对象如何交互以解决一般性的设计问题。它不是具体代码,而是设计思想和指导。
1.2 设计模式的分类
| 类型 | 关注点 | 包含模式 |
|---|---|---|
| 创建型 | 对象的创建过程 | 工厂方法、抽象工厂、单例、建造者、原型 |
| 结构型 | 类和对象的组合 | 适配器、装饰器、代理、外观、桥接、组合、享元 |
| 行为型 | 对象间的交互和职责分配 | 观察者、策略、模板方法、责任链、命令、迭代器、中介者、备忘录、状态、访问者、解释器 |
二、创建型模式
2.1 工厂方法模式 (Factory Method)
意图:定义创建对象的接口,让子类决定实例化哪个类
结构:
┌─────────────┐ ┌─────────────┐
│ Creator │ │ Product │
│ (抽象工厂) │ │ (抽象产品) │
├─────────────┤ └──────▲──────┘
│+factoryMethod()│ │
└──────▲──────┘ │
│ │
│ │
┌──────┴──────┐ ┌───────┴───────┐
│ConcreteCreator│ │ConcreteProduct│
│ (具体工厂) │──→ │ (具体产品) │
├─────────────┤ └───────────────┘
│+factoryMethod()│
└─────────────┘核心代码:
// 抽象工厂
public abstract class Creator {
public abstract Product factoryMethod();
}
// 具体工厂
public class ConcreteCreator extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProduct();
}
}适用场景:
- 一个类不知道它所必须创建的对象的类
- 希望由子类来指定创建的对象
2.2 抽象工厂模式 (Abstract Factory)
意图:提供一个接口,用于创建一系列相关或相互依赖的对象
与工厂方法的区别:
| 模式 | 产品等级 | 产品族 |
|---|---|---|
| 工厂方法 | 单一产品等级 | - |
| 抽象工厂 | 多个产品等级 | 一组相关产品 |
适用场景:
- 系统需要创建一组相关的产品对象
- 需要提供一个产品类库,只暴露接口
2.3 单例模式 (Singleton) ⭐
意图:保证一个类只有一个实例,并提供全局访问点
结构:
┌─────────────────────┐
│ Singleton │
├─────────────────────┤
│ - instance: Singleton│
├─────────────────────┤
│ - Singleton() │ ← 私有构造函数
│ + getInstance() │ ← 公有静态方法
└─────────────────────┘实现方式:
饿汉式(线程安全)
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}懒汉式(需注意线程安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}适用场景:
- 系统只需要一个实例对象
- 如:配置管理器、日志记录器、数据库连接池
2.4 建造者模式 (Builder)
意图:将复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示
适用场景:
- 创建复杂对象,对象由多个部分组成
- 构建过程需要独立于创建对象的类
2.5 原型模式 (Prototype)
意图:用原型实例指定创建对象的种类,通过拷贝这些原型创建新对象
适用场景:
- 创建成本较高,通过复制现有对象更高效
- 需要动态指定创建对象的类型
三、结构型模式
3.1 适配器模式 (Adapter) ⭐
意图:将一个类的接口转换成客户期望的另一个接口
结构:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Client │────→│ Target │ │ Adaptee │
│ │ │(目标接口)│ │(被适配者)│
└─────────┘ └────▲────┘ └────▲────┘
│ │
│ │
┌────┴───────────────┴────┐
│ Adapter │
│ (适配器) │
├─────────────────────────┤
│ - adaptee: Adaptee │
│ + request() │
└─────────────────────────┘核心代码:
// 目标接口
public interface Target {
void request();
}
// 被适配者
public class Adaptee {
public void specificRequest() {
// 原有的方法
}
}
// 适配器
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // 转换调用
}
}适用场景:
- 想使用现有类,但接口不符合需求
- 需要复用不兼容接口的类
3.2 装饰器模式 (Decorator) ⭐
意图:动态地给对象添加额外职责,比继承更灵活
结构:
┌─────────────┐
│ Component │
│ (抽象构件) │
└──────▲──────┘
│
┌────────────┼────────────┐
│ │
┌─────────┴─────────┐ ┌─────────┴─────────┐
│ ConcreteComponent │ │ Decorator │
│ (具体构件) │ │ (抽象装饰) │
└───────────────────┘ ├───────────────────┤
│ - component │
└─────────▲─────────┘
│
┌─────────┴─────────┐
│ ConcreteDecorator │
│ (具体装饰) │
└───────────────────┘核心代码:
// 抽象构件
public interface Component {
void operation();
}
// 具体构件
public class ConcreteComponent implements Component {
@Override
public void operation() {
// 基本功能
}
}
// 抽象装饰器
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addBehavior(); // 添加新行为
}
private void addBehavior() {
// 新增功能
}
}适用场景:
- 不想增加子类来扩展功能
- 动态添加或撤销功能
- 如:Java IO流的设计
3.3 代理模式 (Proxy)
意图:为其他对象提供一种代理以控制对这个对象的访问
代理类型:
- 远程代理
- 虚拟代理
- 保护代理
3.4 外观模式 (Facade)
意图:为子系统中的一组接口提供一个统一的高层接口
适用场景:
- 为复杂子系统提供简单入口
- 客户端与子系统解耦
3.5 桥接模式 (Bridge)
意图:将抽象部分与实现部分分离,使它们可以独立变化
3.6 组合模式 (Composite)
意图:将对象组合成树形结构表示"部分-整体"层次
3.7 享元模式 (Flyweight)
意图:运用共享技术有效支持大量细粒度对象
四、行为型模式
4.1 观察者模式 (Observer) ⭐
意图:定义对象间一对多依赖,当一个对象状态改变时,所有依赖者自动更新
结构:
┌─────────────┐ ┌─────────────┐
│ Subject │ │ Observer │
│ (主题) │◇───────→│ (观察者) │
├─────────────┤ ├─────────────┤
│+attach() │ │+update() │
│+detach() │ └──────▲──────┘
│+notify() │ │
└──────▲──────┘ │
│ │
┌──────┴──────┐ ┌───────┴───────┐
│ConcreteSubject│ │ConcreteObserver│
└─────────────┘ └───────────────┘核心代码:
// 抽象主题
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
// 具体主题
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
// 观察者接口
public interface Observer {
void update(int state);
}适用场景:
- 一个对象改变需要通知其他对象
- 对象之间松散耦合
- 如:GUI事件处理、消息通知
4.2 策略模式 (Strategy) ⭐
意图:定义一系列算法,封装每个算法,使它们可以相互替换
结构:
┌─────────────┐ ┌─────────────┐
│ Context │◇───────→│ Strategy │
│ (环境) │ │ (抽象策略) │
├─────────────┤ └──────▲──────┘
│+setStrategy()│ │
│+execute() │ │
└─────────────┘ ┌───────────┼───────────┐
│ │ │
┌──────┴─────┐ ┌───┴───┐ ┌─────┴──────┐
│StrategyA │ │ ... │ │ StrategyN │
└────────────┘ └───────┘ └────────────┘核心代码:
// 策略接口
public interface Strategy {
void algorithm();
}
// 具体策略
public class ConcreteStrategyA implements Strategy {
@Override
public void algorithm() {
// 算法A
}
}
// 环境类
public class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void execute() {
strategy.algorithm();
}
}适用场景:
- 多种算法,需要动态选择
- 避免多重条件判断
- 如:支付方式选择、排序算法选择
4.3 模板方法模式 (Template Method) ⭐
意图:定义算法骨架,将某些步骤延迟到子类实现
结构:
┌─────────────────────┐
│ AbstractClass │
│ (抽象类) │
├─────────────────────┤
│ + templateMethod() │ ← 定义算法骨架
│ # primitiveOp1() │ ← 抽象方法
│ # primitiveOp2() │ ← 抽象方法
│ # hook() │ ← 钩子方法(可选重写)
└──────────▲──────────┘
│
┌──────────┴──────────┐
│ ConcreteClass │
│ (具体类) │
├─────────────────────┤
│ # primitiveOp1() │ ← 实现具体步骤
│ # primitiveOp2() │
└─────────────────────┘核心代码:
public abstract class AbstractClass {
// 模板方法
public final void templateMethod() {
step1();
step2();
hook();
}
protected abstract void step1();
protected abstract void step2();
// 钩子方法
protected void hook() {
// 默认实现,子类可重写
}
}适用场景:
- 算法骨架固定,具体步骤可变
- 提取公共行为到父类
- 控制子类扩展
4.4 其他行为型模式简介
| 模式 | 意图 |
|---|---|
| 责任链 | 多个对象都有机会处理请求,连成一条链传递 |
| 命令 | 将请求封装为对象,支持撤销、排队 |
| 迭代器 | 顺序访问聚合对象的元素,不暴露内部表示 |
| 中介者 | 用中介对象封装一系列对象交互 |
| 备忘录 | 不破坏封装的前提下捕获对象内部状态 |
| 状态 | 对象状态改变时改变其行为 |
| 访问者 | 不改变类的前提下定义新操作 |
| 解释器 | 给定语言,定义文法表示和解释器 |
五、设计模式对比总结
5.1 创建型模式对比
| 模式 | 核心思想 | 关键特征 |
|---|---|---|
| 工厂方法 | 定义创建接口,子类决定实例化 | 一个产品等级 |
| 抽象工厂 | 创建一系列相关产品 | 多个产品等级,产品族 |
| 单例 | 只有一个实例 | 私有构造,静态方法 |
| 建造者 | 构建与表示分离 | 复杂对象,分步构建 |
| 原型 | 通过拷贝创建 | clone方法 |
5.2 结构型模式对比
| 模式 | 核心思想 | 关键特征 |
|---|---|---|
| 适配器 | 接口转换 | 包装不兼容接口 |
| 装饰器 | 动态添加功能 | 包装同类对象 |
| 代理 | 控制访问 | 与真实对象接口相同 |
| 外观 | 简化接口 | 提供统一入口 |
| 桥接 | 抽象与实现分离 | 两个独立变化维度 |
5.3 行为型模式对比
| 模式 | 核心思想 | 关键特征 |
|---|---|---|
| 观察者 | 一对多依赖,状态变化通知 | 发布-订阅 |
| 策略 | 封装算法,可替换 | 算法族 |
| 模板方法 | 定义骨架,延迟实现 | 继承,抽象方法 |
| 状态 | 状态变化改变行为 | 状态对象 |
| 责任链 | 多个处理者,链式传递 | 处理链 |
六、考试要点
高频考查模式
| 模式 | 考查重点 |
|---|---|
| 单例模式 | 实现方式、线程安全、应用场景 |
| 工厂方法 | 与抽象工厂的区别 |
| 观察者模式 | 结构、实现、应用场景 |
| 适配器模式 | 结构、与装饰器的区别 |
| 装饰器模式 | 结构、与继承的区别 |
| 策略模式 | 结构、与状态模式的区别 |
| 模板方法 | 结构、钩子方法 |
典型例题
题1:某系统需要将不同格式的文件统一转换为PDF格式,应使用什么模式? 答案:适配器模式
题2:需要动态给对象添加日志功能,不修改原有类,应使用什么模式? 答案:装饰器模式
题3:系统需要支持多种支付方式(微信、支付宝、银行卡),并可以动态切换,应使用什么模式? 答案:策略模式
模式识别口诀
创建分离用工厂,唯一实例单例当
适配转换接口异,装饰添加功能强
观察一多状态变,策略封装算法藏
模板骨架子类填,责任链条依次忙