Java设计模式——结构型

设计模式:结构型模式

结构型模式关注的是:类和对象之间如何组合,如何让系统结构更灵活、更容易扩展。

创建型模式解决"对象怎么创建",结构型模式解决"对象怎么组装"。


一、结构型模式总览

结构型模式主要解决以下问题:

  • 类和对象之间如何组合成更大的结构
  • 如何降低对象之间的耦合
  • 如何在不修改原有代码的情况下扩展功能
  • 如何让不兼容接口能够一起工作
  • 如何减少 大量对象带来的内存消耗
模式 核心思想 适合场景
适配器 Adapter 让不兼容接口能一起工作 老接口适配新系统
桥接 Bridge 抽象和实现分离 多维度变化
组合 Composite 树形结构统一处理 文件夹、菜单、组织架构
装饰器 Decorator 动态增强对象功能 IO 流、功能叠加
外观 Facade 给复杂系统提供简单入口 封装复杂子系统
享元 Flyweight 共享对象减少内存 大量重复对象
代理 Proxy 控制对目标对象的访问 权限、缓存、远程代理

结构型模式整体理解图

mindmap root((结构型模式)) 接口适配 适配器 Adapter 多维度解耦 桥接 Bridge 整体部分统一 组合 Composite 功能动态增强 装饰器 Decorator 简化复杂系统 外观 Facade 共享减少内存 享元 Flyweight 控制对象访问 代理 Proxy

二、适配器模式 Adapter

1. 核心思想

把一个类的接口转换成客户端想要的另一个接口。

简单说:

原来接口不兼容,加一个"转换头"。

生活例子:

  • Type-C 转 USB 转接头
  • 港版插头转国标插座
  • 读卡器把 SD 卡转换成 USB 接口

2. 适合场景

  • 旧系统接口不能直接修改
  • 新系统需要使用旧类的功能
  • 第三方接口和自己系统接口不一致
  • 想复用已有代码,但接口不匹配

3. 配图理解

flowchart LR Client[客户端需要 TypeC 接口] --> Adapter[UsbToTypeCAdapter 适配器] Adapter --> Old[UsbCharger 旧接口] Old --> Result[完成 USB 充电]

4. Java 示例

java 复制代码
interface TypeC {
    void chargeWithTypeC();
}

class UsbCharger {
    public void chargeWithUsb() {
        System.out.println("使用 USB 充电");
    }
}

class UsbToTypeCAdapter implements TypeC {
    private UsbCharger usbCharger;

    public UsbToTypeCAdapter(UsbCharger usbCharger) {
        this.usbCharger = usbCharger;
    }

    public void chargeWithTypeC() {
        System.out.println("适配器转换 Type-C 到 USB");
        usbCharger.chargeWithUsb();
    }
}

public class Main {
    public static void main(String[] args) {
        UsbCharger usbCharger = new UsbCharger();
        TypeC adapter = new UsbToTypeCAdapter(usbCharger);

        adapter.chargeWithTypeC();
    }
}

5. 优缺点

优点:

  • 可以复用旧代码
  • 解决接口不兼容问题
  • 客户端不需要知道适配细节
  • 符合开闭原则,可以在不改旧类的情况下新增适配能力

缺点:

  • 适配器太多会让系统结构复杂
  • 只是接口转换,不应该滥用
  • 如果旧接口设计很差,适配器只能缓解问题,不能彻底解决问题

6. 记忆口诀

适配器:接口不合,加个转换器。


三、桥接模式 Bridge

1. 核心思想

抽象 部分和实现 部分分离,使它们可以独立变化。

简单说:

两个维度都在变化时,不要疯狂继承,而是用组合把它们桥接起来。

2. 适合场景

适合两个或多个维度都在变化的场景。

例如手机有两个变化维度:

  • 品牌:华为、小米、苹果
  • 颜色:黑色、白色、蓝色

如果不用桥接,可能要写:

  • 黑色华为手机
  • 白色华为手机
  • 蓝色华为手机
  • 黑色小米手机
  • 白色小米手机
  • 蓝色小米手机

类数量会迅速膨胀。

3. 配图理解

flowchart TB Phone[抽象:Phone 手机] --> Huawei[HuaweiPhone] Phone --> Xiaomi[XiaomiPhone] Phone --> Apple[ApplePhone] Phone -. 持有 .-> Color[实现:Color 颜色接口] Color --> Black[Black 黑色] Color --> White[White 白色] Color --> Blue[Blue 蓝色]

4. Java 示例

java 复制代码
interface Color {
    String getColor();
}

class Black implements Color {
    public String getColor() {
        return "黑色";
    }
}

class White implements Color {
    public String getColor() {
        return "白色";
    }
}

abstract class Phone {
    protected Color color;

    public Phone(Color color) {
        this.color = color;
    }

    public abstract void show();
}

class HuaweiPhone extends Phone {
    public HuaweiPhone(Color color) {
        super(color);
    }

    public void show() {
        System.out.println(color.getColor() + " 华为手机");
    }
}

class XiaomiPhone extends Phone {
    public XiaomiPhone(Color color) {
        super(color);
    }

    public void show() {
        System.out.println(color.getColor() + " 小米手机");
    }
}

public class Main {
    public static void main(String[] args) {
        Phone phone = new HuaweiPhone(new Black());
        phone.show();
    }
}

5. 优缺点

优点:

  • 避免类爆炸
  • 抽象和实现可以独立扩展
  • 比继承更加灵活
  • 符合组合优于继承的思想

缺点:

  • 理解难度比简单继承高
  • 需要正确识别系统中的多个变化维度
  • 设计过度时会让简单问题复杂化

6. 记忆口诀

桥接模式:两个维度,各自变化,中间架桥。


四、组合模式 Composite

1. 核心思想

把对象组织成树形结构,让客户端可以用统一方式处理单个对象和组合对象。

简单说:

单个对象和一组对象,对外表现得像同一种东西。

2. 适合场景

  • 文件和文件夹
  • 公司部门和员工
  • 菜单和子菜单
  • 树形目录
  • 组织架构

3. 配图理解

flowchart TB Root[根目录 Folder] Root --> FileA[文件 a.txt] Root --> Img[图片文件夹 Folder] Img --> Img1[1.png] Img --> Img2[2.png] Root --> Doc[文档文件夹 Folder] Doc --> Doc1[方案.docx]

4. Java 示例

java 复制代码
import java.util.ArrayList;
import java.util.List;

interface FileSystemNode {
    void show();
}

class FileNode implements FileSystemNode {
    private String name;

    public FileNode(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println("文件:" + name);
    }
}

class FolderNode implements FileSystemNode {
    private String name;
    private List<FileSystemNode> children = new ArrayList<>();

    public FolderNode(String name) {
        this.name = name;
    }

    public void add(FileSystemNode node) {
        children.add(node);
    }

    public void show() {
        System.out.println("文件夹:" + name);
        for (FileSystemNode child : children) {
            child.show();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        FolderNode root = new FolderNode("根目录");
        root.add(new FileNode("a.txt"));

        FolderNode img = new FolderNode("图片");
        img.add(new FileNode("1.png"));
        img.add(new FileNode("2.png"));

        root.add(img);
        root.show();
    }
}

5. 优缺点

优点:

  • 适合树形结构
  • 客户端可以统一处理叶子节点和容器节点
  • 扩展方便
  • 层级结构清晰

缺点:

  • 对类型限制不够严格
  • 树形结构太复杂时调试困难
  • 如果叶子对象和容器对象差异太大,统一接口可能会显得勉强

6. 记忆口诀

组合模式:整体和部分,一样对待。


五、装饰器模式 Decorator

1. 核心思想

不改变 原有对象结构的情况下,动态地给对象增加 新的功能

简单说:

原来的对象不改,用一层一层"包装"来增强功能。

生活例子:

  • 奶茶加珍珠、加椰果、加布丁
  • 咖啡加牛奶、加糖、加冰
  • 手机壳给手机增加防摔、支架、卡包功能

2. 适合场景

  • 想给对象动态增加功能
  • 不想通过继承创建大量子类
  • 功能可以一层一层叠加
  • Java IO 流,例如 BufferedInputStream 包装 FileInputStream

3. 配图理解

flowchart LR Simple[普通咖啡] --> Milk[牛奶装饰器] Milk --> Sugar[糖装饰器] Sugar --> Result[普通咖啡 + 牛奶 + 糖]

4. Java 示例

java 复制代码
interface Coffee {
    String getDescription();
    double cost();
}

class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "普通咖啡";
    }

    public double cost() {
        return 10;
    }
}

abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
}

class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    public String getDescription() {
        return coffee.getDescription() + " + 牛奶";
    }

    public double cost() {
        return coffee.cost() + 3;
    }
}

class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    public String getDescription() {
        return coffee.getDescription() + " + 糖";
    }

    public double cost() {
        return coffee.cost() + 1;
    }
}

public class Main {
    public static void main(String[] args) {
        Coffee coffee = new SimpleCoffee();

        coffee = new MilkDecorator(coffee);
        coffee = new SugarDecorator(coffee);

        System.out.println(coffee.getDescription());
        System.out.println("价格:" + coffee.cost());
    }
}

5. 优缺点

优点:

  • 比继承更加灵活
  • 可以动态增加功能
  • 多个装饰器可以自由组合
  • 符合开闭原则

缺点:

  • 装饰层数太多时,代码理解成本会变高
  • 调试时不容易看清楚对象被包装了几层
  • 如果装饰器设计不好,容易让结构复杂化

6. 记忆口诀

装饰器模式:不改原对象,外面套功能。


六、外观模式 Facade

1. 核心思想

给复杂子系统提供一个简单统一的入口,让客户端不用关心内部复杂细节。

简单说:

里面很复杂,外面给你一个简单按钮。

生活例子:

一键开启影院模式:

  • 关灯
  • 打开电视
  • 打开音响
  • 拉上窗帘

用户不需要一个一个操作,只需要点击"影院模式"。

2. 适合场景

  • 子系统很复杂
  • 客户端不想直接接触很多类
  • 想降低客户端和子系统之间的耦合
  • 给旧系统封装一个简单入口

3. 配图理解

flowchart LR Client[客户端] --> Facade[HomeTheaterFacade 外观类] Facade --> Light[灯光系统] Facade --> TV[电视系统] Facade --> Sound[音响系统] Facade --> Curtain[窗帘系统]

4. Java 示例

java 复制代码
class Light {
    public void off() {
        System.out.println("关灯");
    }
}

class TV {
    public void on() {
        System.out.println("打开电视");
    }
}

class SoundSystem {
    public void on() {
        System.out.println("打开音响");
    }
}

class Curtain {
    public void close() {
        System.out.println("拉上窗帘");
    }
}

class HomeTheaterFacade {
    private Light light;
    private TV tv;
    private SoundSystem soundSystem;
    private Curtain curtain;

    public HomeTheaterFacade() {
        this.light = new Light();
        this.tv = new TV();
        this.soundSystem = new SoundSystem();
        this.curtain = new Curtain();
    }

    public void watchMovie() {
        System.out.println("开启影院模式");
        light.off();
        curtain.close();
        tv.on();
        soundSystem.on();
    }
}

public class Main {
    public static void main(String[] args) {
        HomeTheaterFacade facade = new HomeTheaterFacade();
        facade.watchMovie();
    }
}

5. 优缺点

优点:

  • 简化客户端调用
  • 降低 客户端和子系统之间的耦合
  • 隐藏 系统内部复杂细节
  • 让系统更容易使用

缺点:

  • 外观类可能会变得很庞大
  • 如果外观类封装得太死,灵活性会下降
  • 不适合替代所有子系统接口

6. 记忆口诀

外观模式:复杂系统,给个简单门面。


七、享元模式 Flyweight

1. 核心思想

通过共享对象来减少内存占用。

简单说:

相同的对象不要重复创建,能共用就共用。

生活例子:

游戏地图里有很多树:

  • 树的类型、颜色、图片资源可能是一样的
  • 但是每棵树的位置不同

如果每棵树都保存完整数据,会非常浪费内存。享元模式会把相同的部分共享起来,只把不同的部分单独保存。

2. 适合场景

  • 系统中有大量相似对象
  • 对象创建成本高
  • 很多对象的内部状态可以共享
  • 游戏场景、字符串常量池、缓存池、连接池

3. 关键概念

享元模式一般会把对象状态分成两类:

  • 内部状态可以共享,例如树的类型、颜色、图片
  • 外部状态不能共享,例如树的位置坐标

4. 配图理解

flowchart TB Factory[TreeFactory 享元工厂] Factory --> TypeA[共享对象:绿色松树 TreeType] Factory --> TypeB[共享对象:黄色枫树 TreeType] Tree1[树1 x=10 y=20] -. 使用 .-> TypeA Tree2[树2 x=30 y=40] -. 使用 .-> TypeA Tree3[树3 x=50 y=60] -. 使用 .-> TypeA Tree4[树4 x=80 y=90] -. 使用 .-> TypeB

5. Java 示例

java 复制代码
import java.util.HashMap;
import java.util.Map;

class TreeType {
    private String name;
    private String color;

    public TreeType(String name, String color) {
        this.name = name;
        this.color = color;
    }

    public void display(int x, int y) {
        System.out.println("树类型:" + name + ",颜色:" + color + ",位置:(" + x + "," + y + ")");
    }
}

class TreeFactory {
    private static Map<String, TreeType> treeTypes = new HashMap<>();

    public static TreeType getTreeType(String name, String color) {
        String key = name + "_" + color;

        if (!treeTypes.containsKey(key)) {
            treeTypes.put(key, new TreeType(name, color));
            System.out.println("创建新的树类型:" + key);
        }

        return treeTypes.get(key);
    }
}

class Tree {
    private int x;
    private int y;
    private TreeType treeType;

    public Tree(int x, int y, TreeType treeType) {
        this.x = x;
        this.y = y;
        this.treeType = treeType;
    }

    public void display() {
        treeType.display(x, y);
    }
}

public class Main {
    public static void main(String[] args) {
        TreeType greenTree = TreeFactory.getTreeType("松树", "绿色");

        Tree tree1 = new Tree(10, 20, greenTree);
        Tree tree2 = new Tree(30, 40, greenTree);
        Tree tree3 = new Tree(50, 60, greenTree);

        tree1.display();
        tree2.display();
        tree3.display();
    }
}

6. 优缺点

优点:

  • 减少对象数量
  • 节省内存
  • 适合大量重复对象场景
  • 可以提高系统性能

缺点:

  • 代码复杂度会增加
  • 需要区分内部状态和外部状态
  • 如果对象差异很大,就不适合使用

7. 记忆口诀

享元模式:共享重复对象,省内存。


八、代理模式 Proxy

1. 核心思想

给目标对象提供一个代理对象,由代理对象控制对目标对象的访问。

简单说:

你不直接找本人,而是先找代理人。

生活例子:

  • 明星经纪人
  • 房产中介
  • 代购
  • 访问网站时的代理服务器

代理对象可以在真正调用目标对象之前或之后,增加额外逻辑,比如权限校验、缓存、日志、延迟加载。

2. 适合场景

  • 需要控制对象访问权限
  • 需要延迟加载对象
  • 需要添加日志、缓存、事务等增强逻辑
  • 远程代理、虚拟代理、保护代理

3. 配图理解

sequenceDiagram participant Client as 客户端 participant Proxy as 代理对象 ProxyImage participant Real as 真实对象 RealImage Client->>Proxy: display() alt 真实对象还未创建 Proxy->>Real: new RealImage() Real-->>Proxy: 加载完成 end Proxy->>Real: display() Real-->>Client: 显示图片

4. Java 示例

java 复制代码
interface Image {
    void display();
}

class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("从磁盘加载图片:" + fileName);
    }

    public void display() {
        System.out.println("显示图片:" + fileName);
    }
}

class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    public void display() {
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }

        realImage.display();
    }
}

public class Main {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.png");

        System.out.println("图片对象已创建,但还没有真正加载");
        image.display();
        image.display();
    }
}

5. 优缺点

优点:

  • 可以控制目标对象访问
  • 可以延迟加载,节省资源
  • 可以在不修改目标对象的情况下增强功能
  • 适合权限、缓存、日志等场景

缺点:

  • 增加了代理类,代码结构更复杂
  • 调用链路变长,可能影响性能
  • 代理逻辑写多了容易变得臃肿

6. 记忆口诀

代理模式:访问对象前,先找代理人。


九、结构型模式总结

模式 核心思想 一句话记忆
适配器 Adapter 让不兼容接口一起工作 接口不合,加个转换器
桥接 Bridge 抽象和实现分离 两个维度,中间架桥
组合 Composite 树形结构统一处理 整体和部分一样对待
装饰器 Decorator 动态增强对象功能 不改原对象,外面套功能
外观 Facade 提供简单统一入口 复杂系统,给个简单门面
享元 Flyweight 共享对象减少内存 重复对象,能共享就共享
代理 Proxy 控制目标对象访问 访问前,先找代理人

最终记忆图

flowchart LR A[结构型模式] --> B[接口不兼容] B --> B1[适配器] A --> C[多维变化] C --> C1[桥接] A --> D[树形结构] D --> D1[组合] A --> E[功能增强] E --> E1[装饰器] A --> F[简化入口] F --> F1[外观] A --> G[节省内存] G --> G1[享元] A --> H[控制访问] H --> H1[代理]

十、和创建型模式的区别

类型 关注点 一句话理解
创建型模式 对象怎么创建 把 new 解耦出来
结构型模式 对象怎么组合 把类和对象组织起来

创建型像"生产零件",结构型像"组装机器"。

相关推荐
卷无止境2 小时前
C++ 编程的一大坑:非常量全局变量是"万恶之源"
c++·后端
Sinclair3 小时前
认识安企CMS-系统和模板文件结构
后端
柒和远方4 小时前
Phase 7.4 学习博客:为什么多 API 项目需要 Swagger / OpenAPI
前端·后端·架构
柒和远方4 小时前
Phase 7.3 复盘:后台任务不只是“扔进队列”,还要能被看见
前端·后端·架构
易协同低代码4 小时前
通达OA模块开发实战
后端
聂二AI落地内参4 小时前
LLM 数据增强任务卡 4 天:upsert 少传 id 后发生了什么
后端
RainCity4 小时前
Java Swing 自定义组件库分享(十三)
java·笔记·后端
livemetee5 小时前
【关于Spring声明式事务】
java·后端·spring
techdashen6 小时前
Arborium:把 tree-sitter 语法高亮打包成 Rust 文档生态的基础设施
开发语言·后端·rust