设计模式体系

ℹ️本篇定位

设计模式是"面向对象能力"的沉淀,也是读框架源码的钥匙(Spring 就是一部设计模式应用大全)。22 个 GoF 模式不必全背,本篇按创建型 / 结构型 / 行为型挑最常用的讲,并标注哪些已在既有笔记里系统讲过(单例/模板方法/动态代理/策略),避免重复。前置:SOLID 原则

一、总纲:为什么需要模式

设计模式不是"炫技",是对反复出现的对象协作问题的成熟解法。记住 SOLID 是原则、模式是手段:模式解决"变化",把会变的部分隔离起来——这是所有模式的通用思想。

三大类速览:

创建型:对象怎么"造"(单例/工厂/建造者/原型)
结构型:类与对象怎么"搭"(适配器/装饰器/代理/组合)
行为型:对象怎么"协作"(模板方法/策略/观察者/责任链)

二、创建型:对象怎么造

1. 单例模式(已系统讲过)

保证一个类只有一个实例。Java 里最推荐的写法是枚举单例,JVM 天然防反射、防序列化——完整分析见 06-枚举,DCL 与 volatile 的关系见 02-synchronized与volatile

2. 工厂模式:把"new"封装起来

简单工厂 + 工厂方法,把对象创建的判断从调用方挪到工厂:

// 简单工厂:一个工厂根据参数分派
public class MessageFactory {
    public static Message create(String type) {
        return switch (type) {
            case "sms" -> new SmsMessage();
            case "email" -> new EmailMessage();
            default -> throw new IllegalArgumentException(type);
        };
    }
}
Message m = MessageFactory.create("sms");   // 调用方不关心具体类

收益:加一个新类型只改工厂一处,调用方零改动(开闭原则的落地);Spring 的 BeanFactory 本身就是工厂模式的集大成者。

3. 建造者模式:链式构建复杂对象

当对象字段多、可选参数多、还要保证构造过程合法性时,用 Builder 替代一堆构造器重载:

User user = User.builder()
        .name("张三")
        .age(18)
        .email("zhang@example.com")
        .build();   // 链式写法,每个字段可读,build() 统一校验

Java 里最常用的是 Lombok @Builder 注解;手写 Builder 见 02-内部类 的静态内部类应用。

三、结构型:对象怎么搭

4. 适配器模式:把不兼容的接口对上

一个接口转成调用方期望的另一个接口,最典型的是"老系统对接新接口":

// 老接口:drawRectangle(int x, int y, int w, int h)
// 新需求:直接画圆形,但已有 Circle 类画不了矩形接口
class CircleAdapter implements Shape {
    private final Circle circle;
    public void draw(int x, int y) { circle.drawCircle(x, y, circle.radius); }
}

Java 里 InputStreamReader 把字节流适配成字符流、Arrays.asList() 把数组适配成 List,都是适配器。

5. 装饰器模式:运行时动态加功能

不改变类继承,用"包装类套包装类"叠加功能——Java IO 流的经典来源(已系统讲过,见 09-IO流体系):

InputStream in = new BufferedInputStream(new FileInputStream("a.txt"));
// 裸读 → 套缓冲 → 套数据流,功能一层层叠加,类不爆炸

对比代理模式:代理关注访问控制(替身),装饰器关注功能增强(叠 buff),但结构上都是"包一层"。

四、行为型:对象怎么协作

6. 模板方法模式(已讲过)

父类定骨架、子类填空——抽象类与接口结合的最经典形态,见 抽象类与接口的结合;AQS 的 tryAcquire/tryRelease 就是模板方法(04-Lock与AQS)。

7. 策略模式:算法族可切换

把一族可互换的算法封装成接口,运行时选择——Lambda 出现后写法大幅简化(已讲过,见 07-Lambda与函数式接口 第六节):

Map<String, Comparator<Integer>> strategies = Map.of(
    "asc", Integer::compareTo,
    "desc", (a, b) -> b.compareTo(a)
);
list.sort(strategies.get("desc"));

8. 观察者模式:一个变化通知多个订阅者

发布-订阅:被观察者状态一变,自动通知所有观察者。JDK 的 java.util.Observer 已废弃,现代用 Guava EventBus、Spring 的 ApplicationEvent 事件机制替代——但思想一致:解耦"事件源"与"事件处理者"。

// Spring 风格:发布事件 → 所有监听者自动收到
context.publishEvent(new OrderCreatedEvent(order));
@Component
class EmailListener { @EventListener void onOrder(OrderCreatedEvent e) { /* 发邮件 */ } }

五、一句话记忆表

模式 解决什么 一句话识别
单例 全局唯一实例 构造器私有 + 唯一静态实例
工厂 对象创建解耦 new 的逻辑收进一个类
建造者 复杂对象的逐步构造 链式 .name().age().build()
适配器 接口不兼容 换头不换身,把 A 接口转 B 接口
装饰器 运行时加功能 层层套娃,每个套子加一层功能
代理 访问控制 替身挡在前面(Spring AOP/RPC)
模板方法 骨架统一、细节可变 父类 final 骨架,子类填空
策略 算法可切换 算法族 + 运行时选择
观察者 状态变化通知多方 订阅-发布,一对多广播

六、高频面试题速答

  1. 装饰器和代理的区别? 都是"包一层",但装饰器侧重功能增强(层层叠加),代理侧重访问控制(AOP、延迟加载、RPC 桩)。
  2. 策略模式和 if-else 的区别? 策略把每支分支抽成独立类/函数,开闭原则友好;if-else 加分支要改原有代码。
  3. Spring 用了哪些设计模式? 工厂(BeanFactory)、模板方法(JdbcTemplate、AQS 思想的 RestTemplate)、代理(AOP),还有单例(默认作用域)、观察者(事件)。
  4. 单例有几种写法?最推荐哪个? 饿汉/懒汉/DCL/静态内部类/枚举,最推荐枚举(防反射防序列化)。

勾连


⬅️ 03-JDK9-21新特性速览 🏠 00-Java ➡️ Java基础高频面试题