本期内容为自己总结归档,共分十一章,本人遇到过的面试问题会重点标记。
(若有任何疑问,可在评论区告诉我,看到就回复)
一、结构型模式全景
1.1 什么是结构型模式?
结构型模式关注类和对象的组合方式,通过组合不同的类和对象形成更大的结构,同时保持结构的灵活和高效。这些模式描述如何将类或对象结合在一起形成更大的结构,就像使用不同的建筑材料构建复杂的建筑结构。
1.2 结构型模式的分类
结构型模式包括七种模式:适配器模式(Adapter)、桥接模式(Bridge)、组合模式(Composite)、装饰器模式(Decorator)、外观模式(Facade)、享元模式(Flyweight)和代理模式(Proxy)。它们可以分为两类:

类结构型模式 :通过继承来组合接口或实现
对象结构型模式:通过组合对象来形成更大的结构
1.3 结构型模式的共同目标
所有结构型模式都致力于解决以下问题:
-
解耦:减少组件间的依赖
-
复用:提高代码的可复用性
-
灵活性:使系统更容易扩展和修改
-
清晰性:使系统结构更加清晰易懂
二、组合模式(Composite Pattern)
2.1 组合模式的定义
组合模式允许你将对象组合成树形结构来表示"部分-整体"的层次结构,使得客户端可以统一地对待单个对象和组合对象。
2.2 组合模式的核心思想
组合模式通过定义包含基本对象和组合对象的类层次结构,让客户端可以一致地使用组合结构和单个对象。

2.3 组合模式实现:文件系统
java
// 组件接口
public interface FileSystemComponent {
void display(int indent);
long getSize();
String getName();
}
// 叶子节点:文件
public class File implements FileSystemComponent {
private String name;
private long size;
public File(String name, long size) {
this.name = name;
this.size = size;
}
@Override
public void display(int indent) {
String spaces = " ".repeat(indent);
System.out.println(spaces + "📄 " + name + " (" + size + " bytes)");
}
@Override
public long getSize() {
return size;
}
@Override
public String getName() {
return name;
}
}
// 组合节点:目录
public class Directory implements FileSystemComponent {
private String name;
private List<FileSystemComponent> children;
public Directory(String name) {
this.name = name;
this.children = new ArrayList<>();
}
@Override
public void display(int indent) {
String spaces = " ".repeat(indent);
System.out.println(spaces + "- " + name);
// 显示所有子组件
for (FileSystemComponent child : children) {
child.display(indent + 2);
}
}
@Override
public long getSize() {
long totalSize = 0;
for (FileSystemComponent child : children) {
totalSize += child.getSize();
}
return totalSize;
}
@Override
public String getName() {
return name;
}
// 管理子组件的方法
public void add(FileSystemComponent component) {
children.add(component);
}
public void remove(FileSystemComponent component) {
children.remove(component);
}
public FileSystemComponent getChild(int index) {
return children.get(index);
}
public List<FileSystemComponent> getChildren() {
return new ArrayList<>(children);
}
}
// 客户端使用
public class CompositePatternDemo {
public static void main(String[] args) {
System.out.println("=== 组合模式示例:文件系统 ===");
// 创建文件
FileSystemComponent file1 = new File("document.txt", 1024);
FileSystemComponent file2 = new File("image.jpg", 2048);
FileSystemComponent file3 = new File("video.mp4", 10240);
// 创建子目录
Directory subDir = new Directory("Documents");
subDir.add(new File("resume.pdf", 512));
subDir.add(new File("contract.docx", 768));
// 创建主目录
Directory rootDir = new Directory("Root");
rootDir.add(file1);
rootDir.add(file2);
// 创建另一个目录
Directory mediaDir = new Directory("Media");
mediaDir.add(file3);
mediaDir.add(new File("music.mp3", 3072));
// 将子目录添加到主目录
rootDir.add(subDir);
rootDir.add(mediaDir);
// 显示整个结构
System.out.println("\n文件系统结构:");
rootDir.display(0);
// 计算总大小
System.out.println("\n总大小: " + rootDir.getSize() + " bytes");
// 统一处理:对文件和目录执行相同操作
System.out.println("\n遍历所有组件:");
traverse(rootDir, 0);
}
private static void traverse(FileSystemComponent component, int depth) {
component.display(depth);
// 如果是目录,递归遍历子组件
if (component instanceof Directory) {
Directory dir = (Directory) component;
for (FileSystemComponent child : dir.getChildren()) {
traverse(child, depth + 1);
}
}
}
}
3.4 组合模式的特点
优点:
-
简化客户端代码:客户端可以一致地处理简单和复杂元素
-
容易添加新组件:新增组件类型不影响现有结构
-
灵活的结构:可以方便地构建复杂的树形结构
-
符合开闭原则:对扩展开放,对修改封闭
缺点:
-
设计复杂:需要仔细设计组件接口
-
类型安全检查:需要运行时类型检查
-
可能违反单一职责:组件接口可能过于通用
适用场景:
-
表示对象的"部分-整体"层次结构
-
希望客户端忽略组合对象与单个对象的差异
-
树形菜单、文件系统等层次结构
-
GUI组件树(如Swing/JavaFX)
三、代理模式(Proxy Pattern)
3.1 代理模式的定义
代理模式为其他对象提供一种代理以控制对这个对象的访问。代理模式在客户端和目标对象之间起到中介作用,可以用于控制访问、延迟加载、记录日志等。
3.2 代理模式的核心思想
代理模式通过创建一个代理对象来控制对原始对象的访问,可以在不改变原始对象的情况下增加额外的功能。

3.3 代理模式示例:图片加载器
java
// 主题接口:图片
public interface Image {
void display();
String getFileName();
}
// 真实主题:高分辨率图片
public class HighResolutionImage implements Image {
private String fileName;
private byte[] imageData;
public HighResolutionImage(String fileName) {
this.fileName = fileName;
loadImageFromDisk(); // 昂贵的加载操作
}
private void loadImageFromDisk() {
System.out.println("从磁盘加载高分辨率图片: " + fileName);
// 模拟耗时加载过程
try {
Thread.sleep(2000); // 2秒加载时间
imageData = new byte[1024 * 1024]; // 1MB 数据
System.out.println("图片加载完成: " + fileName);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
public void display() {
System.out.println("显示图片: " + fileName + " (大小: " +
(imageData != null ? imageData.length : 0) + " bytes)");
}
@Override
public String getFileName() {
return fileName;
}
}
// 代理:图片代理(虚拟代理)
public class ImageProxy implements Image {
private String fileName;
private HighResolutionImage realImage;
private boolean loaded;
public ImageProxy(String fileName) {
this.fileName = fileName;
this.loaded = false;
System.out.println("创建图片代理: " + fileName);
}
@Override
public void display() {
// 延迟加载:只有在真正需要时才加载图片
if (!loaded) {
loadRealImage();
}
realImage.display();
}
private void loadRealImage() {
if (realImage == null) {
realImage = new HighResolutionImage(fileName);
}
loaded = true;
}
@Override
public String getFileName() {
return fileName;
}
// 代理的其他功能
public void showPlaceholder() {
System.out.println("显示占位符: " + fileName + " (图片未加载)");
}
public boolean isLoaded() {
return loaded;
}
}
// 保护代理:访问控制
public interface SensitiveDocument {
void viewDocument();
void editDocument();
}
public class RealDocument implements SensitiveDocument {
private String content;
private String accessLevel;
public RealDocument(String content, String accessLevel) {
this.content = content;
this.accessLevel = accessLevel;
}
@Override
public void viewDocument() {
System.out.println("查看文档内容: " + content);
}
@Override
public void editDocument() {
System.out.println("编辑文档内容");
content = "已编辑的内容";
}
}
public class DocumentProxy implements SensitiveDocument {
private RealDocument realDocument;
private String userAccessLevel;
public DocumentProxy(String content, String documentAccessLevel,
String userAccessLevel) {
this.realDocument = new RealDocument(content, documentAccessLevel);
this.userAccessLevel = userAccessLevel;
}
@Override
public void viewDocument() {
// 检查访问权限
if (hasAccess("VIEW")) {
realDocument.viewDocument();
} else {
System.out.println("权限不足:无法查看文档");
}
}
@Override
public void editDocument() {
// 检查访问权限
if (hasAccess("EDIT")) {
realDocument.editDocument();
} else {
System.out.println("权限不足:无法编辑文档");
}
}
private boolean hasAccess(String operation) {
// 简单的权限检查逻辑
if ("ADMIN".equals(userAccessLevel)) {
return true;
} else if ("EDITOR".equals(userAccessLevel)) {
return "VIEW".equals(operation) || "EDIT".equals(operation);
} else if ("VIEWER".equals(userAccessLevel)) {
return "VIEW".equals(operation);
}
return false;
}
}
// 客户端使用
public class ProxyPatternDemo {
public static void main(String[] args) {
System.out.println("=== 代理模式示例 ===");
System.out.println("\n1. 虚拟代理示例(图片延迟加载):");
Image image1 = new ImageProxy("photo1.jpg");
Image image2 = new ImageProxy("photo2.jpg");
// 创建代理时不会立即加载图片
System.out.println("图片代理已创建,但未加载真实图片");
// 只有在显示时才加载图片
image1.display(); // 第一次显示,会加载图片
image1.display(); // 第二次显示,使用已加载的图片
System.out.println("\n2. 保护代理示例(访问控制):");
SensitiveDocument confidentialDoc = new DocumentProxy(
"机密内容", "CONFIDENTIAL", "VIEWER"
);
confidentialDoc.viewDocument(); // 可以查看
confidentialDoc.editDocument(); // 权限不足
System.out.println("\n提升权限到 ADMIN:");
SensitiveDocument adminDoc = new DocumentProxy(
"机密内容", "CONFIDENTIAL", "ADMIN"
);
adminDoc.viewDocument();
adminDoc.editDocument();
}
}
3.4 代理模式的类型
| 代理类型 | 目的 | 示例 |
|---|---|---|
| 虚拟代理 | 延迟加载 | 图片代理,直到需要时才加载大图片 |
| 保护代理 | 访问控制 | 权限检查,控制对敏感对象的访问 |
| 远程代理 | 远程访问 | RMI,访问远程对象 |
| 智能引用 | 额外功能 | 引用计数,对象锁定时自动加载 |
3.5 代理模式的特点
优点:
-
控制对象访问:可以在访问对象前后添加额外操作
-
开闭原则:不修改原始对象即可添加新功能
-
延迟加载:可以延迟昂贵对象的创建
-
职责清晰:代理对象专注于控制访问,真实对象专注于业务
缺点:
-
增加系统复杂度:引入额外的代理层
-
可能降低性能:代理调用可能增加额外开销
-
可能过度设计:简单场景不需要代理
适用场景:
-
需要控制对对象的访问
-
需要延迟加载大型对象
-
需要为对象添加额外功能(如日志、缓存)
-
需要保护对象不被直接访问
四、总结
4.1 各模式对比表格
| 模式 | 主要目的 | 关键思想 | 适用场景 |
|---|---|---|---|
| 适配器 | 接口转换 | 包装对象,提供不同接口 | 集成不兼容接口 |
| 桥接 | 分离抽象与实现 | 组合替代继承 | 多维度变化的系统 |
| 组合 | 树形结构 | 部分-整体统一处理 | 层次结构对象 |
| 装饰器 | 动态添加功能 | 包装对象,增强功能 | 运行时功能扩展 |
| 外观 | 简化接口 | 提供统一入口 | 复杂子系统简化 |
| 享元 | 对象共享 | 分离内部/外部状态 | 大量相似对象 |
| 代理 | 控制访问 | 中介控制对象访问 | 访问控制、延迟加载 |