Skip to content
难度基础(★)
建议时长45分钟

10.5.3 结构型设计模式

结构型模式关注类或对象如何组合成更大的结构。它们解决的不是“对象怎么创建”,也不是“算法怎么切换”,而是接口、层次、组合、共享和访问控制的问题。

结构型共有 7 种:适配器、桥接、组合、装饰、外观、享元、代理。

总览表

中文名英文名一句话意图题干关键词
适配器Adapter将一个接口转换成客户希望的另一个接口接口不兼容、转换接口、适配
桥接Bridge将抽象部分与实现部分分离,使二者独立变化两个变化维度、抽象与实现分离
组合Composite用树形结构表示整体-部分,并统一使用方式树形结构、整体-部分、一致使用
装饰Decorator动态给对象添加额外职责动态添加职责、可叠加增强
外观Facade为复杂子系统提供统一高层接口统一入口、简化调用、封装子系统
享元Flyweight共享大量细粒度对象,减少资源开销共享、细粒度、大量对象
代理Proxy为对象提供替身,以控制对对象的访问控制访问、远程代理、虚拟代理、保护代理

适配器:接口转换

适配器解决接口不兼容问题。课程用电源适配器举例:家用电源与设备所需电压不匹配,中间加适配器才能协同工作。翻译器也可以理解为适配器:双方语言不兼容,翻译负责转换。

mermaid
classDiagram
  class Target {
    +request()
  }
  class Adapter {
    +request()
  }
  class Adaptee {
    +specificRequest()
  }
  Target <|.. Adapter
  Adapter --> Adaptee

适配器不是改变原对象内部实现,而是包一层转换接口,让客户按期望接口使用。

桥接:两个维度独立变化

桥接模式把抽象部分和实现部分分离,使它们可以独立变化。它的本质是把一个容易爆炸的多维继承结构拆成两个继承层次,再通过组合连接起来。

例如 Web 应用框架中,一个维度是应用类型:博客、新闻网站、网上商店;另一个维度是主题样式:深色、浅色。如果用继承硬编码,会产生“深色博客、浅色博客、深色商店、浅色商店……”的组合爆炸。桥接让应用类型和主题各自变化,运行时再组合。

mermaid
classDiagram
  class WebApp {
    -Theme theme
    +render()
  }
  class Blog
  class Shop
  class Theme {
    +draw()
  }
  class DarkTheme
  class LightTheme
  WebApp <|-- Blog
  WebApp <|-- Shop
  Theme <|.. DarkTheme
  Theme <|.. LightTheme
  WebApp --> Theme : bridge

桥接常被考成场景题。看到“两个独立变化维度”“不同应用 + 不同主题”“抽象与实现分离”,优先想到桥接。

组合:树形整体-部分

组合模式用树形结构表示整体-部分关系,使客户对单个对象和组合对象具有一致的使用方式。

典型例子是目录结构:文件和文件夹都可以被打开、移动、删除。文件是叶子,文件夹是组合节点,但使用者可以按统一接口操作。

mermaid
classDiagram
  class Component {
    +operation()
  }
  class Leaf
  class Composite {
    +add(component)
    +remove(component)
    +operation()
  }
  Component <|.. Leaf
  Component <|.. Composite
  Composite o-- Component

组合模式的关键不是“组合”这个日常词,而是树形结构、整体-部分、一致对待。

装饰:动态添加职责

装饰模式在不改变原对象的前提下,动态附加额外职责。课程用饮品加配料解释:不必为所有配料组合都派生子类,而是把珍珠、椰果、布丁等作为装饰对象,按需要叠加。

mermaid
classDiagram
  class Component {
    +operation()
  }
  class ConcreteComponent
  class Decorator {
    -Component component
    +operation()
  }
  class ConcreteDecoratorA
  class ConcreteDecoratorB
  Component <|.. ConcreteComponent
  Component <|.. Decorator
  Decorator <|-- ConcreteDecoratorA
  Decorator <|-- ConcreteDecoratorB
  Decorator --> Component

装饰比继承更灵活,因为职责可以运行时组合。它解决的是“功能组合爆炸”的问题。

外观:统一入口

外观模式定义一个高层接口,把复杂子系统封装起来,对外提供一致入口。客户不需要知道内部七八个子系统如何协作,只需要调用外观接口。

mermaid
flowchart LR
  Client["客户程序"] --> Facade["Facade统一入口"]
  Facade --> A["子系统A"]
  Facade --> B["子系统B"]
  Facade --> C["子系统C"]

外观和适配器都可能出现“接口”二字,但含义不同:

对比项适配器外观
解决问题原接口不兼容子系统太复杂
主要动作转换接口简化调用入口
是否重点改造子系统不改原接口,做转换不要求修改子系统,做统一封装

享元:共享细粒度对象

享元模式支持大量细粒度对象的共享,以减少对象数量和资源开销。课程用文字举例:如果英文字符可以共享 26 个字母对象,再用外部状态组合出文章,就能避免为每个字符位置都创建全新对象。

适合场景:

  • 系统中存在大量相似小对象。
  • 对象内部状态可以共享。
  • 外部状态可由上下文提供。
  • 创建大量对象会造成明显资源压力。

享元被新方案替代的常见原因是:现代运行时和内存管理比过去更强,很多场景不再值得为极小对象共享增加复杂度。但在字体、图形、游戏对象池、缓存等高频对象场景,它仍有价值。

代理:控制访问

代理模式为真实对象提供一个替身,由代理控制对真实对象的访问。代理与真实对象通常实现同一接口,客户通过代理访问目标。

mermaid
classDiagram
  class Subject {
    +request()
  }
  class RealSubject
  class Proxy {
    -RealSubject realSubject
    +request()
  }
  Subject <|.. RealSubject
  Subject <|.. Proxy
  Proxy --> RealSubject

常见代理:

类型用途
远程代理代表不同地址空间中的对象
虚拟代理延迟创建昂贵对象,或缓存目标信息
保护代理检查权限,控制访问

代理和中介者容易混淆:代理只是控制对某个对象的访问;中介者会改变多个对象之间的交互方式。

结构型模式易混辨析

易混组区分关键
适配器 vs 外观适配器转换不兼容接口;外观封装复杂子系统,提供统一入口
装饰 vs 代理装饰给对象增加职责;代理控制访问
组合 vs 装饰组合是树形整体-部分;装饰是运行时叠加功能
桥接 vs 访问者桥接把两个变化维度组合起来;访问者选择一种访问方式遍历对象结构

例题

单选
将一个类的接口转换成客户希望的另一个接口,属于:
单选
一个 Web 应用有“应用类型”和“主题样式”两个独立变化维度,希望二者可以独立扩展并组合,适合使用:

自查要点

  1. 桥接模式为什么能避免多维继承爆炸?
  2. 组合模式中的“整体”和“部分”为什么要统一接口?
  3. 装饰和代理都包了一层对象,它们的目的有什么不同?
  4. 外观和适配器都涉及接口,为什么不是同一种问题?