--- title: "04-设计模式体系" created: 2026-09-03 tags: - Java --- # 设计模式体系 > [!note] 本篇定位 > 设计模式是"面向对象能力"的沉淀,也是读框架源码的钥匙(Spring 就是一部设计模式应用大全)。22 个 GoF 模式不必全背,本篇按**创建型 / 结构型 / 行为型**挑最常用的讲,并标注哪些已在既有笔记里系统讲过(单例/模板方法/动态代理/策略),避免重复。前置:[[07-面向对象设计的SOLID原则|SOLID 原则]]。 ## 一、总纲:为什么需要模式 设计模式不是"炫技",是对**反复出现的对象协作问题**的成熟解法。记住 SOLID 是原则、模式是手段:模式解决"变化",把会变的部分隔离起来——这是所有模式的通用思想。 三大类速览: ```text 创建型:对象怎么"造"(单例/工厂/建造者/原型) 结构型:类与对象怎么"搭"(适配器/装饰器/代理/组合) 行为型:对象怎么"协作"(模板方法/策略/观察者/责任链) ``` ## 二、创建型:对象怎么造 ### 1. 单例模式(已系统讲过) 保证一个类只有一个实例。Java 里最推荐的写法是**枚举单例**,JVM 天然防反射、防序列化——完整分析见 [[06-枚举|06-枚举]],DCL 与 volatile 的关系见 [[02-synchronized与volatile|02-synchronized与volatile]]。 ### 2. 工厂模式:把"new"封装起来 **简单工厂 + 工厂方法**,把对象创建的判断从调用方挪到工厂: ```java // 简单工厂:一个工厂根据参数分派 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 替代一堆构造器重载: ```java User user = User.builder() .name("张三") .age(18) .email("zhang@example.com") .build(); // 链式写法,每个字段可读,build() 统一校验 ``` Java 里最常用的是 Lombok `@Builder` 注解;手写 Builder 见 [[02-内部类|02-内部类]] 的静态内部类应用。 ## 三、结构型:对象怎么搭 ### 4. 适配器模式:把不兼容的接口对上 一个接口转成调用方期望的另一个接口,最典型的是"老系统对接新接口": ```java // 老接口: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流体系|09-IO流体系]]): ```java InputStream in = new BufferedInputStream(new FileInputStream("a.txt")); // 裸读 → 套缓冲 → 套数据流,功能一层层叠加,类不爆炸 ``` 对比代理模式:代理关注**访问控制**(替身),装饰器关注**功能增强**(叠 buff),但结构上都是"包一层"。 ## 四、行为型:对象怎么协作 ### 6. 模板方法模式(已讲过) 父类定骨架、子类填空——抽象类与接口结合的最经典形态,见 [[02-抽象类与接口的结合|抽象类与接口的结合]];AQS 的 tryAcquire/tryRelease 就是模板方法([[04-Lock与AQS|04-Lock与AQS]])。 ### 7. 策略模式:算法族可切换 把一族可互换的算法封装成接口,运行时选择——Lambda 出现后写法大幅简化(已讲过,见 [[07-Lambda与函数式接口|07-Lambda与函数式接口]] 第六节): ```java Map> 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` 事件机制替代——但思想一致:解耦"事件源"与"事件处理者"。 ```java // 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/静态内部类/枚举,最推荐枚举(防反射防序列化)。 ## 勾连 - 面向对象与 SOLID 原则:[[07-面向对象设计的SOLID原则|SOLID原则]] - 模式背后的 Java 语言机制:[[02-内部类|内部类]](单例/建造者)、[[05-动态代理|动态代理]] --- ⬅️ [[03-JDK9-21新特性速览|03-JDK9-21新特性速览]] 🏠 [[00-Java|00-Java]] ➡️ [[01-Java基础高频面试题|Java基础高频面试题]]