📌 PDF :AI人工智能 --- AI Agent智能体实战项目
大白话说Java设计模式-17-装饰器模式(源码剖析篇):JDK / Spring 中的装饰器实现
📌 一句话本质:装饰器在源码里不是"穿衣服"的形象化,而是"链式包装 + 同接口"的工业级架构。
🏷️ 标签:装饰器模式 / JDK IO / Collections 包装 / Spring BeanDefinitionDecorator 🎯 适合:中高级后端 / 想读懂主流框架源码的工程师
目录
- 一、为什么读装饰器源码?一张地图先看清
- [二、JDK 源码剖析:IO 装饰器家族](#二、JDK 源码剖析:IO 装饰器家族)
- [三、JDK 源码剖析:Collections 装饰器](#三、JDK 源码剖析:Collections 装饰器)
- [四、Spring 源码剖析:BeanDefinitionDecorator](#四、Spring 源码剖析:BeanDefinitionDecorator)
- 五、为什么框架作者这么设计?
- [六、借鉴到我们项目:3 个"抄作业"实践](#六、借鉴到我们项目:3 个"抄作业"实践)
- [七、本篇小结 + 下一模式预告](#七、本篇小结 + 下一模式预告)
一、为什么读装饰器源码?一张地图先看清
装饰器模式在源码里出现得最经典 ------Java IO 流就是教科书级实现。
源码地图:
| 类别 | 框架 / 中间件 | 装饰器 | 用途 |
|---|---|---|---|
| JDK | java.io |
BufferedInputStream |
缓冲 |
| JDK | java.io |
DataInputStream |
按类型读 |
| JDK | java.io |
GZIPInputStream |
解压 |
| JDK | java.io |
ObjectInputStream |
对象序列化 |
| JDK | java.io |
PrintStream |
格式化输出 |
| JDK | java.util |
Collections.synchronizedList |
线程安全 |
| JDK | java.util |
Collections.unmodifiableList |
不可变 |
| JDK | java.util |
Collections.checkedList |
类型检查 |
| Spring | Spring Beans | BeanDefinitionDecorator |
装饰 Bean 定义 |
| Spring | Spring AOP | IntroductionInfo |
引入新接口 |
| MyBatis | MyBatis | Cache 装饰器链 |
缓存装饰 |
| Servlet | Servlet API | HttpServletRequestWrapper |
包装请求 |
我们按"JDK IO → JDK Collections → Spring"的顺序来剖析。
二、JDK 源码剖析:IO 装饰器家族
2.1 FilterInputStream:装饰器基类
源码位置:
- OpenJDK 17
src/java.base/share/classes/java/io/FilterInputStream.java
核心源码:
java
/**
* ✅ 装饰器基类:所有 IO 装饰器的父类
*/
public class FilterInputStream extends InputStream {
/**
* ✅ 持有被装饰者引用
*/
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
/**
* 默认实现:调被装饰者的方法
*/
@Override
public int read() throws IOException {
return in.read();
}
@Override
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return in.skip(n);
}
@Override
public int available() throws IOException {
return in.available();
}
@Override
public void close() throws IOException {
in.close();
}
}
关键点:
| 角色 | 类 |
|---|---|
| 抽象组件 | InputStream |
| 装饰器基类 | FilterInputStream |
| 装饰器子类 | BufferedInputStream / DataInputStream / ... |
2.2 BufferedInputStream:缓冲装饰器
源码位置:
- OpenJDK 17
src/java.base/share/classes/java/io/BufferedInputStream.java
核心源码(简化版):
java
/**
* ✅ 装饰器:缓冲流
* <p>
* 默认 8192 字节缓冲区,提升 IO 性能
*/
public class BufferedInputStream extends FilterInputStream {
/**
* 缓冲区
*/
protected volatile byte[] buf;
/**
* 缓冲区大小
*/
protected int bufSize; // 默认 8192
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE); // 8192
}
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
this.bufSize = size;
this.buf = new byte[size];
}
/**
* ✅ 装饰器核心:填充缓冲区
*/
private void fill() throws IOException {
byte[] buffer = (buf == null) ? new byte[bufSize] : buf;
int n = in.read(buffer, 0, bufSize);
// ...
}
/**
* ✅ 重写 read:先从缓冲区读
*/
@Override
public int read() throws IOException {
// 先从缓冲区读
if (pos >= count) {
fill(); // 缓冲区空,从上游流读
}
return getBufIfOpen()[pos++] & 0xff;
}
@Override
public int read(byte b[], int off, int len) throws IOException {
// 优先从缓冲区读
// 不够时再从上游流读
// ...
}
}
关键点:
| 关键点 | 解释 |
|---|---|
extends FilterInputStream |
继承装饰器基类 |
super(in) |
持有被装饰者 |
重写 read() |
加缓冲逻辑 |
不改变 InputStream 接口 |
装饰器核心 |
2.3 DataInputStream:类型装饰器
源码位置:
- OpenJDK 17
src/java.base/share/classes/java/io/DataInputStream.java
核心源码(简化版):
java
/**
* ✅ 装饰器:数据流(按类型读取)
*/
public class DataInputStream extends FilterInputStream implements DataInput {
public DataInputStream(InputStream in) {
super(in);
}
/**
* 读取 boolean
*/
public final boolean readBoolean() throws IOException {
int ch = in.read();
if (ch < 0) throw new EOFException();
return (ch != 0);
}
/**
* 读取 int
*/
public final int readInt() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
/**
* 读取 UTF 字符串
*/
public final String readUTF() throws IOException {
// 读取 UTF-8 编码的字符串
// ...
}
}
关键点:
| 关键点 | 解释 |
|---|---|
implements DataInput |
加新接口(按类型读) |
readBoolean() |
装饰器加新方法 |
readInt() |
装饰器加新方法 |
不破坏 InputStream 接口 |
仍能当 InputStream 用 |
2.4 GZIPInputStream:解压装饰器
源码位置:
- OpenJDK 17
src/java.base/share/classes/java/util/zip/GZIPInputStream.java
核心源码(简化版):
java
/**
* ✅ 装饰器:GZIP 解压流
*/
public class GZIPInputStream extends InflaterInputStream {
public GZIPInputStream(InputStream in) throws IOException {
super(in, new Inflater(), 512);
// 读取 GZIP 头
readHeader(in);
}
@Override
public int read(byte[] buf, int off, int len) throws IOException {
// 调父类(InflaterInputStream)的 read,自动解压
return super.read(buf, off, len);
}
}
/**
* InflaterInputStream:通用解压流(装饰器)
*/
public class InflaterInputStream extends FilterInputStream {
protected final Inflater inf; // 解压器
protected byte[] buf; // 解压缓冲区
public InflaterInputStream(InputStream in, Inflater inf, int size) {
super(in);
if (in == null || inf == null) throw new NullPointerException();
this.inf = inf;
this.buf = new byte[size];
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
// 调上游读 + 解压
// ...
}
}
关键点:
| 关键点 | 解释 |
|---|---|
extends InflaterInputStream |
继承通用解压装饰器 |
super(in, new Inflater(), 512) |
持有上游 + 初始化解压器 |
重写 read |
加 GZIP 头解析 |
2.5 完整 IO 装饰器链
java
/**
* ✅ 真实使用:链式装饰
*/
InputStream fileIn = new FileInputStream("data.txt");
InputStream bufferedIn = new BufferedInputStream(fileIn); // 缓冲
DataInputStream dataIn = new DataInputStream(bufferedIn); // 类型
InputStream gzIn = new GZIPInputStream(dataIn); // 解压
ObjectInputStream objIn = new ObjectInputStream(gzIn); // 对象
// 装饰链:
// ObjectInputStream → GZIPInputStream → DataInputStream → BufferedInputStream → FileInputStream
装饰链结构:
ObjectInputStream(对象流)
↓ in = GZIPInputStream
GZIPInputStream(解压流)
↓ in = DataInputStream
DataInputStream(数据流)
↓ in = BufferedInputStream
BufferedInputStream(缓冲流)
↓ in = FileInputStream
FileInputStream(文件流)
2.6 装饰器为什么设计得好?
关键点:
| 特性 | 解释 |
|---|---|
| 同接口 | 所有流继承 InputStream |
| 持有引用 | protected InputStream in |
| 链式调用 | in.read() 层层向上 |
| 可嵌套 | 任意组合 |
| 可扩展 | 继承 FilterInputStream 加新装饰器 |
三、JDK 源码剖析:Collections 装饰器
3.1 Collections.synchronizedList:线程安全装饰器
源码位置:
- OpenJDK 17
src/java.base/share/classes/java/util/Collections.java
核心源码(简化版):
java
/**
* ✅ 装饰器:synchronized 集合
*/
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
/**
* SynchronizedList 内部类
*/
static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {
static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
/**
* ✅ 装饰器核心:所有方法加 synchronized
*/
@Override
public E get(int index) {
synchronized (mutex) {
return list.get(index);
}
}
@Override
public E set(int index, E element) {
synchronized (mutex) {
return list.set(index, element);
}
}
@Override
public void add(int index, E element) {
synchronized (mutex) {
list.add(index, element);
}
}
@Override
public E remove(int index) {
synchronized (mutex) {
return list.remove(index);
}
}
@Override
public int size() {
synchronized (mutex) {
return list.size();
}
}
}
关键点:
| 关键点 | 解释 |
|---|---|
extends SynchronizedCollection |
继承同步集合基类 |
super(list) |
持有被装饰者 |
每个方法加 synchronized |
装饰器加的逻辑 |
mutex 锁 |
同步控制 |
完整装饰链:
java
List<String> list = new ArrayList<>(); // 节点
list = Collections.synchronizedList(list); // 装饰 1:线程安全
// list = Collections.unmodifiableList(list); // 装饰 2:不可变
// 调用 list.add("a") 会加锁
// 调用 list.get(0) 会加锁
3.2 Collections.unmodifiableList:不可变装饰器
核心源码(简化版):
java
/**
* ✅ 装饰器:不可变集合
*/
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
/**
* UnmodifiableList 内部类
*/
static class UnmodifiableList<E> extends UnmodifiableCollection<E> implements List<E> {
static final long serialVersionUID = -2546718832117073025L;
final List<? extends E> list;
UnmodifiableList(List<? extends E> list) {
super(list);
this.list = list;
}
/**
* ✅ 装饰器核心:所有修改方法抛异常
*/
@Override
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
@Override
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
@Override
public E remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public E get(int index) {
return list.get(index); // 读操作不抛
}
}
关键点:
| 关键点 | 解释 |
|---|---|
| 所有修改方法抛异常 | 装饰器加的逻辑 |
| 读方法透传 | 不破坏读操作 |
| 返回的 List 接口相同 | 同接口 |
3.3 Collections.checkedList:类型检查装饰器
核心源码(简化版):
java
/**
* ✅ 装饰器:类型检查集合
*/
public static <E> List<E> checkedList(List<E> list, Class<E> type) {
return (list instanceof RandomAccess ?
new CheckedRandomAccessList<>(list, type) :
new CheckedList<>(list, type));
}
/**
* CheckedList 内部类
*/
static class CheckedList<E> extends CheckedCollection<E> implements List<E> {
final List<E> list;
CheckedList(List<E> list, Class<E> type) {
super(list, type);
this.list = list;
}
/**
* ✅ 装饰器核心:插入时检查类型
*/
@Override
public void add(int index, E element) {
typeCheck(element); // ✅ 类型检查
list.add(index, element);
}
@Override
public E set(int index, E element) {
typeCheck(element);
return list.set(index, element);
}
void typeCheck(Object o) {
if (o != null && !type.isInstance(o)) {
throw new ClassCastException(badElementMsg(o));
}
}
}
关键点:
| 关键点 | 解释 |
|---|---|
| 插入时类型检查 | 装饰器加的逻辑 |
typeCheck |
核心方法 |
| 防类型污染 | 应对 Java 泛型擦除 |
3.4 Collections 装饰器对比表
| 装饰器 | 加的功能 | 装饰方法 |
|---|---|---|
synchronizedList |
线程安全 | synchronized |
unmodifiableList |
不可变 | 抛异常 |
checkedList |
类型检查 | typeCheck |
unmodifiableSortedSet |
不可变 + 排序 | 抛异常 + 保持顺序 |
业务启示:
Collections 装饰器是"Java 标准装饰器模式" 。需要线程安全?包一层
synchronizedList。需要不可变?包一层unmodifiableList。任意组合,互不干扰。
四、Spring 源码剖析:BeanDefinitionDecorator
4.1 BeanDefinitionDecorator 接口
源码位置:
- Spring Framework 6.1.x
spring-beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionDecorator.java
核心源码:
java
/**
* ✅ Spring 装饰器:装饰 Bean 定义
*/
public interface BeanDefinitionDecorator {
/**
* 装饰 BeanDefinition
*/
BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext);
}
关键点:
| 角色 | 类 |
|---|---|
| 抽象组件 | BeanDefinitionHolder |
| 装饰器 | BeanDefinitionDecorator(接口) |
| 具体装饰器 | DefaultBeanDefinitionDecorator 等 |
4.2 DefaultBeanDefinitionDecorator:默认装饰器
源码位置:
spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDecorator.java
核心源码(简化版):
java
/**
* ✅ Spring 默认 BeanDefinition 装饰器
*/
public class DefaultBeanDefinitionDecorator implements BeanDefinitionDecorator {
private static final String ATTRIBUTE_EQUAL = "=";
private static final String ATTRIBUTE_PROPERTY = "property";
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
// 1️⃣ 解析属性
// 2️⃣ 应用到 BeanDefinition
// 3️⃣ 返回装饰后的 BeanDefinition
// 例如 <bean id="user" class="User" p:age="20"/>
// 这里会解析 p:age 属性,设置到 BeanDefinition
return definition;
}
}
关键点:
| 关键点 | 解释 |
|---|---|
| 装饰 Bean 定义 | 在 Bean 创建前装饰 |
| 不影响 Bean 类 | 只改 BeanDefinition |
| 自定义装饰器 | 实现 BeanDefinitionDecorator 接口 |
4.3 Spring 用装饰器模式的 3 个场景
| 场景 | 装饰器 | 作用 |
|---|---|---|
| Bean 属性装饰 | DefaultBeanDefinitionDecorator |
解析 p: 属性 |
| 事务装饰 | @Transactional |
加事务拦截器 |
| 缓存装饰 | @Cacheable |
加缓存拦截器 |
@Transactional 的本质是装饰器:
java
/**
* Spring 事务 = 装饰器模式
*/
@Service
public class OrderService {
@Transactional // ✅ 装饰:加事务
public void createOrder() {
// 业务代码
}
}
完整装饰链:
OrderService(业务)
↓ 装饰
TransactionInterceptor(事务)
↓ 装饰
LogAspect(日志)
↓ 装饰
最终代理对象
五、为什么框架作者这么设计?
读源码看的是"实现",悟的是"设计哲学"。
5.1 框架作者选装饰器的 4 个核心考虑
| 序号 | 考虑 | 举例 |
|---|---|---|
| ① | 动态叠加功能 | Java IO 流 |
| ② | 同接口不破坏 | Collections 装饰器 |
| ③ | 可任意组合 | Buffered + Data + GZIP + Object |
| ④ | 加新功能不改老 | 加新装饰器不动原有 |
5.2 装饰器 vs 继承:JDK 的选择
Java IO 早期有人提过用继承扩展,但:
BufferedFileInputStream extends FileInputStreamDataFileInputStream extends FileInputStreamGZIPFileInputStream extends FileInputStream- 组合起来:
BufferedDataGZIPFileInputStream extends ???
类数量爆炸,无法维护。
改用装饰器:
BufferedInputStream装饰FileInputStreamDataInputStream装饰BufferedInputStreamGZIPInputStream装饰DataInputStream- 任意组合,类数量恒定
5.3 框架作者不选装饰器的 3 种情况
| 序号 | 场景 | 原因 |
|---|---|---|
| ① | 只加一个功能 | 装饰器是多余的 |
| ② | 功能影响接口 | 用继承 |
| ③ | 顺序固定 | 用普通方法 |
5.4 装饰器在企业级应用的 3 种典型用法
用法 1:数据流加工
JSON → 压缩 → 加密 → Base64
→ 任意组合
用法 2:报表导出
Excel → 加图表 → 加边框 → 加水印
→ 任意组合
用法 3:权限校验链
登录 → 角色校验 → 资源权限校验
→ 任一失败即拒绝
六、借鉴到我们项目:3 个"抄作业"实践
6.1 实践 1:数据导出流
场景:大白商城的数据导出支持 JSON / Excel / 压缩 / 加密任意组合。
完整实现:
java
/**
* 借鉴 Java IO:数据导出装饰器
*/
public abstract class DataExporter {
public abstract byte[] export(Object data);
}
/**
* 节点导出器:JSON
*/
public class JsonExporter extends DataExporter {
@Override
public byte[] export(Object data) {
return JSON.toJSONBytes(data);
}
}
/**
* 装饰器:压缩
*/
public class CompressExporter extends DataExporter {
private final DataExporter wrapped;
public CompressExporter(DataExporter wrapped) {
this.wrapped = wrapped;
}
@Override
public byte[] export(Object data) {
byte[] raw = wrapped.export(data);
// GZIP 压缩
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPOutputStream gzos = new GZIPOutputStream(baos)) {
gzos.write(raw);
} catch (IOException e) {
throw new RuntimeException("压缩失败", e);
}
return baos.toByteArray();
}
}
/**
* 装饰器:加密
*/
public class EncryptExporter extends DataExporter {
private final DataExporter wrapped;
private final String key;
public EncryptExporter(DataExporter wrapped, String key) {
this.wrapped = wrapped;
this.key = key;
}
@Override
public byte[] export(Object data) {
byte[] raw = wrapped.export(data);
// AES 加密
// ...
return encrypted;
}
}
// 使用:JSON → 压缩 → 加密
DataExporter exporter = new JsonExporter();
exporter = new CompressExporter(exporter);
exporter = new EncryptExporter(exporter, "secret_key");
byte[] result = exporter.export(data);
收益:
| 优化点 | 效果 |
|---|---|
| 任意组合 | JSON 可加可不加密,可选压缩 |
| 加新格式 | 加新 Exporter,链不动 |
| 代码简洁 | 没有 if-else |
6.2 实践 2:商品 VO 装饰器
场景:商品详情 VO 包含基础信息 + 标签 + 图片 + 规格,可按需叠加。
完整实现:
java
/**
* 借鉴 Collections 装饰器:商品 VO 装饰器
*/
public abstract class GoodsVoDecorator implements GoodsVoSupplier {
protected final GoodsVoSupplier wrapped;
public GoodsVoDecorator(GoodsVoSupplier wrapped) {
this.wrapped = wrapped;
}
@Override
public GoodsVo get() {
return wrapped.get();
}
}
/**
* 装饰器:加标签
*/
public class WithTagsDecorator extends GoodsVoDecorator {
public WithTagsDecorator(GoodsVoSupplier wrapped) {
super(wrapped);
}
@Override
public GoodsVo get() {
GoodsVo vo = super.get();
vo.setTags(List.of("热销", "新品", "包邮"));
return vo;
}
}
/**
* 装饰器:加图片
*/
public class WithImagesDecorator extends GoodsVoDecorator {
public WithImagesDecorator(GoodsVoSupplier wrapped) {
super(wrapped);
}
@Override
public GoodsVo get() {
GoodsVo vo = super.get();
vo.setImages(List.of("img1.jpg", "img2.jpg"));
return vo;
}
}
// 使用:基础 VO + 标签 + 图片
GoodsVoSupplier supplier = new BaseGoodsVo();
supplier = new WithTagsDecorator(supplier);
supplier = new WithImagesDecorator(supplier);
GoodsVo vo = supplier.get();
6.3 实践 3:日志装饰器
场景:日志支持控制台输出 / 文件输出 / 远程上报,任意组合。
完整实现:
java
/**
* 借鉴 Java IO 装饰器:日志装饰器
*/
public abstract class Logger {
public abstract void log(String message);
}
/**
* 节点:内存日志
*/
public class MemoryLogger extends Logger {
private final List<String> logs = new ArrayList<>();
@Override
public void log(String message) {
logs.add(message);
}
}
/**
* 装饰器:控制台输出
*/
public class ConsoleLogger extends Logger {
private final Logger wrapped;
public ConsoleLogger(Logger wrapped) {
this.wrapped = wrapped;
}
@Override
public void log(String message) {
wrapped.log(message);
System.out.println("[CONSOLE] " + message);
}
}
/**
* 装饰器:文件输出
*/
public class FileLogger extends Logger {
private final Logger wrapped;
private final String filePath;
public FileLogger(Logger wrapped, String filePath) {
this.wrapped = wrapped;
this.filePath = filePath;
}
@Override
public void log(String message) {
wrapped.log(message);
// 写入文件
// Files.write(Paths.get(filePath), message.getBytes(), StandardOpenOption.APPEND);
}
}
// 使用:内存 + 控制台 + 文件
Logger logger = new MemoryLogger();
logger = new ConsoleLogger(logger);
logger = new FileLogger(logger, "/var/log/app.log");
logger.log("Hello World");
// 输出:内存 + 控制台 + 文件 三个地方都有
收益:
| 优化点 | 效果 |
|---|---|
| 任意组合 | 内存/控制台/文件可选 |
| 加新输出 | 加新 Logger 装饰器 |
| 配置化 | 运行时选择装饰链 |
七、本篇小结 + 下一模式预告
7.1 本篇小结(5 个核心要点)
- JDK IO 装饰器 :
FilterInputStream装饰器基类,BufferedInputStream/DataInputStream/GZIPInputStream叠加功能,教科书级实现。 - Collections 装饰器 :
synchronizedList/unmodifiableList/checkedList,任意组合。 - Spring 装饰器 :
@Transactional本质是装饰器,事务 = 装饰器模式。 - 设计哲学 :同接口 + 持有引用 + 链式调用 + 可嵌套。
- 借鉴价值 :数据导出 / 商品 VO / 日志,任意叠加用装饰器。
7.2 一句话总结
装饰器在源码里不是"穿衣服"的形象化,而是"链式包装 + 同接口"的工业级架构 。Java IO 流是教科书,Collections 装饰器是另一经典,Spring 事务本质也是装饰器。学装饰器,先学 Java IO。
7.3 知识脑图
装饰器源码剖析
├── JDK IO
│ ├── FilterInputStream(基类)
│ ├── BufferedInputStream(缓冲)
│ ├── DataInputStream(类型)
│ ├── GZIPInputStream(解压)
│ └── ObjectInputStream(对象)
├── JDK Collections
│ ├── synchronizedList(线程安全)
│ ├── unmodifiableList(不可变)
│ └── checkedList(类型检查)
├── Spring
│ ├── BeanDefinitionDecorator(Bean 装饰)
│ └── @Transactional(事务装饰)
└── 借鉴价值
├── 数据导出流
├── 商品 VO 装饰器
└── 日志装饰器
7.4 下一模式预告
第 18 篇【外观模式 - 业务实战篇】:大白商城下单门面的"一站式服务"
下一篇我们会实战外观模式,回答 4 个问题:
- 大白商城下单为什么要"一站式服务"?
- 外观 vs 适配器 / 中介者怎么区分?
- SLF4J 的
LoggerFactory怎么借鉴外观? - Spring 的
JdbcTemplate怎么借鉴外观?
并附完整的下单门面实战代码(含订单/库存/支付/物流 4 个子系统 + 外观 + 单元测试),可直接复制到 IDEA 跑。
觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯