Skip to content

设计模式

GoF(Gang of Four)23 种经典设计模式详解,聚焦 Java 开发中最常用的模式。


一、为什么学习设计模式

1.1 设计模式的价值

  • 复用解决方案:避免重复发明轮子,直接使用经过验证的设计
  • 统一沟通语言:团队中说"这里用策略模式"比描述具体实现更高效
  • 面向对象设计原则的实践:模式是对 SOLID 原则的具体应用
  • 提高代码可扩展性:好的模式使代码易于扩展,对修改封闭(开闭原则)

1.2 六大设计原则 (SOLID + 1)

原则缩写含义
单一职责原则SRP一个类只负责一件事
开闭原则OCP对扩展开放,对修改关闭
里氏替换原则LSP子类可以替换父类而不影响程序正确性
接口隔离原则ISP不应强迫客户端依赖它不需要的接口
依赖倒置原则DIP依赖抽象而非具体实现
迪米特法则LoD最少知识原则,减少类之间的耦合

1.3 设计模式分类

分类模式数量
创建型单例、工厂方法、抽象工厂、建造者、原型5
结构型适配器、桥接、组合、装饰器、外观、享元、代理7
行为型责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者11

二、创建型模式

2.1 单例模式 (Singleton)

意图:确保一个类只有一个实例,并提供全局访问点。

四种实现方式对比

java
// 1. 饿汉式 -- 类加载时初始化,线程安全,但可能浪费内存
public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    private EagerSingleton() {}
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

// 2. 双重检查锁定 -- 懒加载,线程安全,volatile 防止指令重排
public class DoubleCheckSingleton {
    private static volatile DoubleCheckSingleton instance;
    private DoubleCheckSingleton() {}
    public static DoubleCheckSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

// 3. 静态内部类 -- 懒加载,线程安全,利用类加载机制
public class HolderSingleton {
    private HolderSingleton() {}
    private static class Holder {
        private static final HolderSingleton INSTANCE = new HolderSingleton();
    }
    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;
    }
}

// 4. 枚举 -- 最安全,防止反射和序列化破坏
public enum EnumSingleton {
    INSTANCE;

    public void doSomething() {
        // 业务方法
    }
}

何时使用:全局配置管理、数据库连接池、日志工厂、Spring Bean(默认单例)。

面试要点volatile 防止指令重排(new 对象不是原子操作:分配内存 -> 初始化 -> 赋值引用);枚举单例自动防止反射攻击和序列化破坏。

2.2 工厂方法模式 (Factory Method)

意图:定义一个创建对象的接口,让子类决定实例化哪个类。

java
// 产品接口
interface Product {
    void use();
}

// 具体产品
class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("使用产品 A");
    }
}

class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用产品 B");
    }
}

// 工厂接口
interface Factory {
    Product createProduct();
}

// 具体工厂
class FactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

class FactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 使用
Factory factory = new FactoryA();
Product product = factory.createProduct();
product.use();

何时使用:当创建逻辑较复杂,或需要根据条件创建不同子类时。Spring 中的 FactoryBean 就是工厂方法模式的典型应用。

2.3 抽象工厂模式 (Abstract Factory)

意图:创建一系列相关或相互依赖的对象,无需指定具体类。

java
// 产品族:按钮和文本框
interface Button { void render(); }
interface TextField { void render(); }

// Windows 风格产品族
class WindowsButton implements Button {
    @Override public void render() { System.out.println("Windows 按钮"); }
}
class WindowsTextField implements TextField {
    @Override public void render() { System.out.println("Windows 文本框"); }
}

// Mac 风格产品族
class MacButton implements Button {
    @Override public void render() { System.out.println("Mac 按钮"); }
}
class MacTextField implements TextField {
    @Override public void render() { System.out.println("Mac 文本框"); }
}

// 抽象工厂
interface GUIFactory {
    Button createButton();
    TextField createTextField();
}

// 具体工厂 -- Windows 产品族
class WindowsFactory implements GUIFactory {
    @Override public Button createButton() { return new WindowsButton(); }
    @Override public TextField createTextField() { return new WindowsTextField(); }
}

// 具体工厂 -- Mac 产品族
class MacFactory implements GUIFactory {
    @Override public Button createButton() { return new MacButton(); }
    @Override public TextField createTextField() { return new MacTextField(); }
}

与工厂方法模式的区别:工厂方法创建单个产品,抽象工厂创建产品族。抽象工厂 = 一组工厂方法。

2.4 建造者模式 (Builder)

意图:将复杂对象的构建与表示分离,使用相同的构建过程创建不同的表示。

java
@Data
public class Computer {
    private String cpu;
    private String memory;
    private String disk;
    private String gpu;
    private String os;

    // 私有构造函数
    private Computer() {}

    // 静态建造者
    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private Computer computer = new Computer();

        public Builder cpu(String cpu) {
            computer.cpu = cpu;
            return this;
        }

        public Builder memory(String memory) {
            computer.memory = memory;
            return this;
        }

        public Builder disk(String disk) {
            computer.disk = disk;
            return this;
        }

        public Builder gpu(String gpu) {
            computer.gpu = gpu;
            return this;
        }

        public Builder os(String os) {
            computer.os = os;
            return this;
        }

        public Computer build() {
            // 可以在这里做校验
            if (computer.cpu == null) {
                throw new IllegalStateException("CPU 不能为空");
            }
            return computer;
        }
    }
}

// 使用 -- 流式 API
Computer pc = Computer.builder()
    .cpu("Intel i7")
    .memory("32GB")
    .disk("1TB SSD")
    .gpu("RTX 4070")
    .os("Windows 11")
    .build();

何时使用:构造函数参数过多(>4 个)、参数可选、需要不可变对象、需要流式 API。Lombok 的 @Builder 注解可自动生成。


三、结构型模式

3.1 适配器模式 (Adapter)

意图:将一个接口转换为客户端期望的另一个接口,使原本不兼容的类可以一起工作。

java
// 已存在的旧接口(无法修改)
interface OldLogger {
    void logMessage(String msg);
}

class OldLoggerImpl implements OldLogger {
    @Override
    public void logMessage(String msg) {
        System.out.println("OLD: " + msg);
    }
}

// 新的目标接口
interface NewLogger {
    void info(String message);
    void error(String message, Throwable t);
}

// 适配器 -- 将旧接口适配为新接口
class LoggerAdapter implements NewLogger {
    private final OldLogger oldLogger;

    public LoggerAdapter(OldLogger oldLogger) {
        this.oldLogger = oldLogger;
    }

    @Override
    public void info(String message) {
        oldLogger.logMessage("[INFO] " + message);
    }

    @Override
    public void error(String message, Throwable t) {
        oldLogger.logMessage("[ERROR] " + message + ": " + t.getMessage());
    }
}

何时使用:整合遗留系统、对接第三方库、统一多个不同接口。Spring 的 HandlerAdapter 就是适配器模式。

3.2 装饰器模式 (Decorator)

意图:动态地给对象添加额外的职责,比继承更灵活。

java
// Java IO 就是装饰器模式的经典应用
// 组件接口
interface DataSource {
    String readData();
}

// 具体组件
class FileDataSource implements DataSource {
    private String data;
    public FileDataSource(String data) { this.data = data; }
    @Override
    public String readData() { return data; }
}

// 装饰器基类
abstract class DataSourceDecorator implements DataSource {
    protected final DataSource wrappee;
    public DataSourceDecorator(DataSource source) { this.wrappee = source; }
}

// 加密装饰器
class EncryptionDecorator extends DataSourceDecorator {
    public EncryptionDecorator(DataSource source) { super(source); }
    @Override
    public String readData() {
        return decrypt(wrappee.readData());
    }
    private String decrypt(String data) {
        return "解密后的: " + data;
    }
}

// 压缩装饰器
class CompressionDecorator extends DataSourceDecorator {
    public CompressionDecorator(DataSource source) { super(source); }
    @Override
    public String readData() {
        return decompress(wrappee.readData());
    }
    private String decompress(String data) {
        return "解压后的: " + data;
    }
}

// 使用 -- 多层装饰
DataSource source = new CompressionDecorator(
    new EncryptionDecorator(
        new FileDataSource("原始数据")
    )
);
source.readData();  // 解压后的: 解密后的: 原始数据

Java IO 中的装饰器模式

java
// FileInputStream 是具体组件,BufferedInputStream 是装饰器
InputStream in = new BufferedInputStream(
    new DataInputStream(
        new FileInputStream("data.bin")
    )
);

3.3 代理模式 (Proxy)

意图:为另一个对象提供替身或占位符以控制对它的访问。

java
// 接口
interface UserService {
    User findById(Long id);
    void save(User user);
}

// 真实实现
class UserServiceImpl implements UserService {
    @Override
    public User findById(Long id) {
        // 模拟数据库查询
        return new User(id, "user" + id);
    }

    @Override
    public void save(User user) {
        System.out.println("保存用户: " + user);
    }
}

// 代理 -- 添加缓存
class CachingUserServiceProxy implements UserService {
    private final UserService target;
    private final Map<Long, User> cache = new ConcurrentHashMap<>();

    public CachingUserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public User findById(Long id) {
        return cache.computeIfAbsent(id, target::findById);
    }

    @Override
    public void save(User user) {
        cache.put(user.getId(), user);
        target.save(user);
    }
}

// 使用
UserService service = new CachingUserServiceProxy(new UserServiceImpl());

Spring AOP 中的代理:Spring 使用 JDK 动态代理(接口代理)和 CGLIB 代理(类代理)实现 AOP 功能,如事务管理、日志切面等。


四、行为型模式

4.1 策略模式 (Strategy)

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

java
// 策略接口
interface PaymentStrategy {
    void pay(BigDecimal amount);
}

// 具体策略
class AlipayStrategy implements PaymentStrategy {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付: " + amount);
    }
}

class WechatPayStrategy implements PaymentStrategy {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("微信支付: " + amount);
    }
}

class CreditCardStrategy implements PaymentStrategy {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("信用卡支付: " + amount);
    }
}

// 上下文 -- 使用策略
class PaymentContext {
    private PaymentStrategy strategy;

    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void executePayment(BigDecimal amount) {
        strategy.pay(amount);
    }
}

// 使用 -- 替代 if-else
PaymentContext context = new PaymentContext();
context.setStrategy(new AlipayStrategy());
context.executePayment(new BigDecimal("100.00"));

策略模式 + 枚举优化

java
enum PaymentType {
    ALIPAY(new AlipayStrategy()),
    WECHAT(new WechatPayStrategy()),
    CREDIT_CARD(new CreditCardStrategy());

    private final PaymentStrategy strategy;

    PaymentType(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void pay(BigDecimal amount) {
        strategy.pay(amount);
    }
}

// 使用 -- 一行代码替代 if-else
PaymentType.valueOf(paymentMethod).pay(amount);

4.2 观察者模式 (Observer)

意图:定义对象间的一对多依赖,当一个对象状态改变时,所有依赖者自动收到通知。

java
// 观察者接口
interface Observer {
    void update(String event);
}

// 具体观察者
class EmailListener implements Observer {
    @Override
    public void update(String event) {
        System.out.println("发送邮件: " + event);
    }
}

class SmsListener implements Observer {
    @Override
    public void update(String event) {
        System.out.println("发送短信: " + event);
    }
}

// 被观察者(主题)
class OrderSubject {
    private final List<Observer> observers = new ArrayList<>();

    public void register(Observer observer) {
        observers.add(observer);
    }

    public void unregister(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String event) {
        for (Observer observer : observers) {
            observer.update(event);
        }
    }

    public void createOrder() {
        // 创建订单逻辑
        notifyObservers("订单已创建");
    }
}

Spring 事件机制ApplicationEvent + @EventListener 就是观察者模式的实现。

4.3 模板方法模式 (Template Method)

意图:定义算法骨架,将某些步骤延迟到子类实现。

java
// 抽象类 -- 定义算法骨架
abstract class DataProcessor {
    // 模板方法 -- final 防止子类修改
    public final void process() {
        loadData();
        validateData();
        transformData();
        saveData();
        afterProcess();  // 钩子方法
    }

    protected abstract void loadData();

    protected void validateData() {
        // 默认实现,子类可以覆盖
        System.out.println("默认校验...");
    }

    protected abstract void transformData();

    protected abstract void saveData();

    // 钩子方法 -- 子类可选覆盖
    protected void afterProcess() {
        // 默认什么都不做
    }
}

// 具体实现
class CsvProcessor extends DataProcessor {
    @Override
    protected void loadData() {
        System.out.println("加载 CSV 文件");
    }

    @Override
    protected void transformData() {
        System.out.println("转换 CSV 数据");
    }

    @Override
    protected void saveData() {
        System.out.println("保存到数据库");
    }

    @Override
    protected void afterProcess() {
        System.out.println("发送处理完成通知");
    }
}

框架中的应用:Spring 的 AbstractApplicationContext(refresh 方法)、JdbcTemplate、MyBatis 的 BaseExecutor

4.4 责任链模式 (Chain of Responsibility)

意图:将请求沿着处理链传递,直到被某个处理器处理。

java
// 处理器接口
abstract class Handler {
    protected Handler next;

    public Handler setNext(Handler next) {
        this.next = next;
        return next;
    }

    public void handle(Request request) {
        if (doHandle(request)) {
            return;
        }
        if (next != null) {
            next.handle(request);
        }
    }

    protected abstract boolean doHandle(Request request);
}

// 具体处理器
class AuthenticationHandler extends Handler {
    @Override
    protected boolean doHandle(Request request) {
        if (!request.isAuthenticated()) {
            System.out.println("认证失败");
            return true;  // 结束链
        }
        System.out.println("认证通过");
        return false;  // 继续传递
    }
}

class RateLimitHandler extends Handler {
    @Override
    protected boolean doHandle(Request request) {
        if (exceedLimit(request)) {
            System.out.println("限流拒绝");
            return true;
        }
        System.out.println("限流通过");
        return false;
    }
    private boolean exceedLimit(Request request) { return false; }
}

class BusinessHandler extends Handler {
    @Override
    protected boolean doHandle(Request request) {
        System.out.println("执行业务逻辑");
        return true;
    }
}

// 使用 -- 构建链
Handler chain = new AuthenticationHandler();
chain.setNext(new RateLimitHandler())
     .setNext(new BusinessHandler());

chain.handle(request);

框架中的应用:Servlet Filter 链、Spring Security 的过滤器链、Spring Interceptor 拦截器链、Netty 的 ChannelPipeline。


五、设计模式速查表

模式分类一句话描述框架应用
单例创建型全局唯一实例Spring Bean 默认作用域
工厂方法创建型子类决定创建哪个对象Spring FactoryBean
抽象工厂创建型创建产品族JDBC Connection
建造者创建型分步构建复杂对象Lombok @Builder, StringBuilder
原型创建型克隆对象Java Cloneable, BeanUtils.copy
适配器结构型接口转换Spring HandlerAdapter
装饰器结构型动态添加职责Java IO 流
代理结构型控制对象访问Spring AOP, MyBatis Mapper
外观结构型提供统一接口SLF4J
组合结构型树形结构文件系统、菜单
享元结构型共享细粒度对象Integer 缓存, 连接池
桥接结构型分离抽象与实现JDBC Driver
策略行为型算法族可替换Comparator, 支付方式
观察者行为型状态变化通知Spring Event, 消息队列
模板方法行为型定义算法骨架AbstractApplicationContext
责任链行为型沿链传递请求Servlet Filter, Spring Security
状态行为型状态驱动行为订单状态机
命令行为型请求封装为对象Runnable, 线程池任务
迭代器行为型遍历集合Java Iterator
中介者行为型协调对象交互MVC 中的 Controller
备忘录行为型保存/恢复状态Undo 操作
访问者行为型分离算法与结构ASM 字节码操作
解释器行为型解析语法正则表达式, SQL 解析

面试常见问题

Q1: 单例模式有几种实现方式?推荐哪种?

A: 四种主要方式:饿汉式、双重检查锁定(DCL)、静态内部类、枚举。推荐使用枚举单例,因为它最简单、线程安全、自动防止反射攻击和序列化破坏,是 Joshua Bloch 在《Effective Java》中推荐的实现方式。如果枚举不适用,静态内部类(Holder 模式)是第二选择。

Q2: 策略模式和工厂模式有什么区别?

A: 策略模式关注的是行为的选择,封装一系列可互换的算法,运行时动态切换。工厂模式关注的是对象的创建,封装实例化逻辑,返回合适的对象。策略模式解决的是"怎么做"的问题,工厂模式解决的是"创建哪个"的问题。两者常结合使用:工厂创建策略对象,策略执行具体行为。

Q3: 装饰器模式和代理模式的区别?

A: 装饰器模式是为了增强功能,装饰者和被装饰者实现同一接口,装饰者可以多层嵌套,关注于"在某功能上叠加更多功能"。代理模式是为了控制访问,代理控制对真实对象的访问,关注于"在访问真实对象前后做额外操作"(如 AOP 事务、日志)。从使用目的区分:装饰器是"加功能",代理是"控访问"。

Q4: 模板方法模式和策略模式的区别?

A: 模板方法使用继承,在父类中定义算法骨架,子类实现具体步骤,编译时确定行为。策略模式使用组合,定义策略接口,运行时动态切换算法。模板方法适合"整体流程固定,个别步骤可变"的场景;策略模式适合"算法族整体可替换"的场景。模板方法更强调流程控制,策略模式更强调算法替换。

Q5: 责任链模式在 Spring Security 中如何体现?

A: Spring Security 的过滤器链(SecurityFilterChain)是责任链模式的典型实现。每个过滤器(如 UsernamePasswordAuthenticationFilterCsrfFilter)都负责一项安全检查,可以决定处理请求、修改请求或传递给下一个过滤器。这种设计使得安全策略可以灵活组合和配置,增删安全检查项无需修改核心代码。