结构型模式关注如何将类和对象组合成更大的结构,同时保持结构的灵活和高效率。


1. 适配器模式(Adapter)

意图

将一个类的接口转换成客户希望的另一个接口。适配器使原本因接口不兼容而不能一起工作的类可以一起工作。

问题

现有类(Adaptee)的接口与客户端期望的目标接口(Target)不匹配。例如,你希望复用一个已有的日志库,但它输出的格式不符合你系统要求的统一接口。

解决方案

  • 创建适配器(Adapter) 类实现目标(Target)接口。
  • 适配器内部持有一个适配者(Adaptee)对象的引用。
  • 适配器将目标接口的调用转换为对适配者相应方法的调用。

结构(UML)

┌─────────────┐       ┌─────────────┐
│   Target    │       │   Adaptee   │
├─────────────┤       ├─────────────┤
│ + request() │       │ + specificRequest() │
└─────────────┘       └─────────────┘
        △                       △
        │                       │
┌─────────────┐       ┌─────────────────┐
│   Adapter   │──────▶│   (holds adaptee)│
├─────────────┤       └─────────────────┘
│ + request() │
└─────────────┘

参与者

  • Target:客户端期望使用的接口。
  • Adaptee:需要适配的已有类,有特殊接口。
  • Adapter:实现 Target 接口,内部调用 Adaptee 的方法。

协作

  • 客户端通过 Target 接口调用 Adapter 的方法。
  • Adapter 将调用转发给 Adaptee,完成适配。

适用性

  • 需要使用现有类,但其接口不符合需求。
  • 希望创建一个可复用的类,与不相关或不可预见的类协作。
  • (对象适配器)需要适配多个 Adaptee 及其子类。

Python 示例

# Adaptee: 不兼容的接口
class EuropeanSocket:
    def voltage(self): return 230
    def live(self): return "live wire"
    def neutral(self): return "neutral"
 
# Target: 客户端期望的接口
class USASocket:
    def voltage(self): return 120
    def hot(self): return "hot"
    def neutral(self): return "neutral"
 
# Adapter
class Adapter(USASocket):
    def __init__(self, euro_socket: EuropeanSocket):
        self._euro = euro_socket
 
    def voltage(self):
        return 120   # 转换电压
 
    def hot(self):
		return "hot" # self._euro.live()    映射 live -> hot
 
# Client
def plug_in(socket: USASocket):
    print(f"Plugging with {socket.voltage()}V, hot: {socket.hot()}")
 
euro = EuropeanSocket()
adapter = Adapter(euro)
plug_in(adapter)

2. 桥接模式(Bridge)

意图

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

问题

当抽象可能有多种实现方式时,如果用继承,类层次会急剧膨胀且难以维护。例如:形状(圆形、方形)和颜色(红、蓝)的组合——用继承需要 2×2=4 个类;若增加形状或颜色,类数量爆炸。

解决方案

  • 将抽象(Abstraction)与实现(Implementor)分离,使用组合而非继承。
  • Abstraction 持有 Implementor 的引用,客户通过 Abstraction 操作,具体行为委托给 Implementor。
  • 两者可独立扩展,在运行时动态组合。

结构(UML)

┌─────────────┐          ┌─────────────┐
│ Abstraction │──────────│ Implementor │
├─────────────┤          ├─────────────┤
│ + operation()│          │ + operationImpl()│
└─────────────┘          └─────────────┘
        △                         △
┌─────────────┐          ┌─────────────┐
│RefinedAbstraction│        │ConcreteImplementorA│
└─────────────┘          └─────────────┘

参与者

  • Abstraction:定义抽象接口,维护一个指向 Implementor 的引用。
  • RefinedAbstraction:扩展 Abstraction 的接口。
  • Implementor:定义实现类接口,通常只提供基本操作。
  • ConcreteImplementor:实现 Implementor 接口。

协作

  • Abstraction 将客户端请求转发给其 Implementor 对象。

适用性

  • 不希望抽象和实现之间有一个固定的绑定关系。
  • 抽象和实现都应该可以通过子类化独立扩展。
  • 对抽象的实现部分的修改应对客户不产生影响。

Python 示例

# Implementor
class DrawingAPI:
    def draw_circle(self, x, y, radius): pass
 
# ConcreteImplementors
class DrawingAPI1(DrawingAPI):
    def draw_circle(self, x, y, radius):
        print(f"API1 drawing circle at ({x},{y}) radius {radius}")
 
class DrawingAPI2(DrawingAPI):
    def draw_circle(self, x, y, radius):
        print(f"API2 drawing circle at ({x},{y}) radius {radius}")
 
# Abstraction
class Shape:
    def __init__(self, drawing_api: DrawingAPI):
        self._drawing_api = drawing_api
    def draw(self): pass
    def resize(self, factor): pass
 
# RefinedAbstraction
class Circle(Shape):
    def __init__(self, x, y, radius, drawing_api):
        super().__init__(drawing_api)
        self._x = x
        self._y = y
        self._radius = radius
    def draw(self):
        self._drawing_api.draw_circle(self._x, self._y, self._radius)
    def resize(self, factor):
        self._radius *= factor
 
# Client
circle1 = Circle(1, 2, 3, DrawingAPI1())
circle1.draw()
circle2 = Circle(5, 7, 11, DrawingAPI2())
circle2.draw()

3. 组合模式(Composite)

意图

将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使客户端对单个对象和组合对象的使用具有一致性。

问题

处理树形结构(如文件系统、UI 控件)时,叶节点和容器节点需要不同处理,代码会有大量 if 判断。

解决方案

  • 定义一个组件(Component) 接口,叶节点(Leaf)和容器(Composite)都实现该接口。
  • 容器对象包含子组件列表,并实现添加、删除子组件的方法。
  • 客户端统一使用 Component 接口操作,无需区分叶子和容器。

结构(UML)

┌─────────────┐
│  Component  │
├─────────────┤
│ + operation()│
│ + add(c)    │ (可选)
│ + remove(c) │
└─────────────┘
        △
   ┌────┴────┐
┌───────┐ ┌───────┐
│ Leaf  │ │Composite│
├───────┤ ├───────┤
│ + operation││ + operation│
└───────┘ │ + add()   │
          │ + remove()│
          └───────────┘

参与者

  • Component:为组合中的对象声明接口,可实现默认行为。
  • Leaf:表示叶节点对象,没有子节点。
  • Composite:定义有子节点的行为,存储子组件,实现子组件相关操作。

协作

  • 客户端通过 Component 接口操作组合结构,对单个对象和组合对象一视同仁。

适用性

  • 表示对象的“部分-整体”层次结构。
  • 希望客户端忽略组合对象与单个对象的不同。

Python 示例

from abc import ABC, abstractmethod
 
# Component
class Graphic(ABC):
    @abstractmethod
    def draw(self): pass
 
# Leaf
class Circle(Graphic):
    def draw(self): print("Drawing Circle")
 
class Rectangle(Graphic):
    def draw(self): print("Drawing Rectangle")
 
# Composite
class CompositeGraphic(Graphic):
    def __init__(self):
        self._children = []
    def add(self, graphic: Graphic):
        self._children.append(graphic)
    def remove(self, graphic: Graphic):
        self._children.remove(graphic)
    def draw(self):
        for child in self._children:
            child.draw()
 
# Client
leaf1 = Circle()
leaf2 = Rectangle()
composite = CompositeGraphic()
composite.add(leaf1)
composite.add(leaf2)
composite.draw()

4. 装饰器模式(Decorator)

意图

动态地给一个对象添加额外的职责。相比生成子类,装饰器模式更灵活。

问题

用继承扩展功能是静态的,且子类数量可能爆炸。例如:给窗口添加滚动条、边框、阴影等功能,子类组合会很多。

解决方案

  • 定义组件(Component) 接口。
  • 具体组件(ConcreteComponent) 实现该接口。
  • 装饰器(Decorator) 类也实现 Component 接口,并持有一个 Component 的引用。
  • 具体装饰器(ConcreteDecorator)扩展装饰器,在调用前后添加行为。

结构(UML)

┌─────────────┐
│  Component  │
├─────────────┤
│ + operation()│
└─────────────┘
        △
┌─────────────┐
│  Decorator  │──────▶ (holds Component)
├─────────────┤
│ + operation()│
└─────────────┘
        △
┌─────────────┐
│ConcreteDecoratorA│
├─────────────┤
│ + operation()│
│ + addedBehavior()│
└─────────────┘

参与者

  • Component:定义对象接口。
  • ConcreteComponent:原始对象,可以动态添加职责。
  • Decorator:维持指向 Component 的引用,并实现 Component 接口。
  • ConcreteDecorator:添加额外职责。

协作

  • Decorator 将请求转发给 Component,并可能在转发前后执行额外操作。

适用性

  • 在不影响其他对象的情况下,动态、透明地给单个对象添加职责。
  • 需要扩展一个类的功能,但生成子类不可行(如类被标记为 final,或子类数量膨胀)。

Python 示例

# Component
class Coffee:
    def cost(self): return 5
    def description(self): return "Coffee"
 
# Decorator
class CoffeeDecorator(Coffee):
    def __init__(self, coffee: Coffee):
        self._coffee = coffee
    def cost(self):
        return self._coffee.cost()
    def description(self):
        return self._coffee.description()
 
# ConcreteDecorators
class MilkDecorator(CoffeeDecorator):
    def cost(self):
        return self._coffee.cost() + 1
    def description(self):
        return self._coffee.description() + ", Milk"
 
class SugarDecorator(CoffeeDecorator):
    def cost(self):
        return self._coffee.cost() + 0.5
    def description(self):
        return self._coffee.description() + ", Sugar"
 
# Client
my_coffee = Coffee()
my_coffee = MilkDecorator(my_coffee)
my_coffee = SugarDecorator(my_coffee)
print(my_coffee.description(), "cost:", my_coffee.cost())

5. 外观模式(Facade)

意图

为子系统中的一组接口提供一个统一的简化接口。外观模式定义了一个高层接口,使子系统更易使用。

问题

复杂子系统有多个类,客户端需要了解很多细节才能使用。比如启动计算机需要 CPU、内存、硬盘等对象协同操作。

解决方案

  • 引入外观(Facade) 类,它提供简单的接口(如 start()),内部封装子系统的复杂交互。
  • 客户端只需与外观交互,无需了解子系统内部。

结构(UML)

┌─────────────┐
│   Client    │
└──────┬──────┘
       │
┌──────▼──────┐      ┌─────────────────┐
│   Facade    │─────▶│ Subsystem classes │
└─────────────┘      └─────────────────┘

参与者

  • Facade:知道哪些子系统类负责处理请求,将客户端请求委派给适当的子系统对象。
  • Subsystem classes:实现子系统的功能,处理 Facade 指派的任务,但不知道 Facade 的存在。

协作

  • 客户端调用 Facade 的方法,Facade 将调用转发给子系统。

适用性

  • 需要为复杂子系统提供简单接口。
  • 希望将客户端与子系统解耦,降低编译依赖。
  • 需要分层系统中为每层定义入口点。

Python 示例

# Subsystem classes
class CPU:
    def freeze(self): print("CPU freeze")
    def jump(self, position): print(f"CPU jump to {position}")
    def execute(self): print("CPU execute")
 
class Memory:
    def load(self, position, data): print(f"Memory load {data} to {position}")
 
class HardDrive:
    def read(self, lba, size): return f"boot sector data"
 
# Facade
class ComputerFacade:
    def __init__(self):
        self._cpu = CPU()
        self._memory = Memory()
        self._hd = HardDrive()
    def start(self):
        self._cpu.freeze()
        boot_data = self._hd.read(0, 1024)
        self._memory.load(0, boot_data)
        self._cpu.jump(0)
        self._cpu.execute()
 
# Client
computer = ComputerFacade()
computer.start()

6. 享元模式(Flyweight)

意图

运用共享技术有效地支持大量细粒度的对象。

问题

当系统中存在大量相似对象(如文字处理器中的每个字符),每个对象存储全部数据会消耗大量内存。

解决方案

  • 将对象的状态分为内部状态(可共享,不随环境改变)和外部状态(不可共享,由客户端传递)。
  • 享元工厂维护一个享元池,客户端请求时若存在则返回共享对象,否则创建新对象。
  • 客户端负责传递外部状态。

结构(UML)

┌─────────────┐       ┌─────────────┐
│  FlyweightFactory│────▶│   Flyweight  │
├─────────────┤       ├─────────────┤
│ + getFlyweight(key)│    │ + operation(extrinsicState)│
└─────────────┘       └─────────────┘
                              △
                       ┌─────────────┐
                       │ConcreteFlyweight│
                       ├─────────────┤
                       │ - intrinsicState│
                       │ + operation(extrinsic)│
                       └─────────────┘

参与者

  • Flyweight:声明接口,接受外部状态。
  • ConcreteFlyweight:实现 Flyweight 接口,存储内部状态。
  • FlyweightFactory:创建并管理享元对象,确保共享。
  • Client:维护外部状态,并调用享元对象。

协作

  • 客户端不直接实例化 ConcreteFlyweight,而是通过工厂获取。
  • 客户端将外部状态传递给享元的 operation 方法。

适用性

  • 大量对象且内存开销大。
  • 对象的大多数状态可以变为外部状态。
  • 删除外部状态后,可以用相对较少的共享对象替代。

Python 示例

# Flyweight
class Character:
    def __init__(self, char_code: str):
        self._char_code = char_code   # intrinsic state
    def display(self, font_size: int):   # extrinsic state
        print(f"Character '{self._char_code}' with font size {font_size}")
 
# FlyweightFactory
class CharacterFactory:
    _pool = {}
    @classmethod
    def get_character(cls, char_code: str):
        if char_code not in cls._pool:
            cls._pool[char_code] = Character(char_code)
        return cls._pool[char_code]
 
# Client
def render_text(text: str, font_size: int):
    for ch in text:
        char = CharacterFactory.get_character(ch)
        char.display(font_size)
 
render_text("hello", 12)

7. 代理模式(Proxy)

意图

为其他对象提供一种代理以控制对这个对象的访问。

问题

直接访问一个对象可能带来问题:例如远程访问开销大、需要延迟加载大对象、或者需要控制访问权限。

解决方案

  • 代理(Proxy)实现与真实对象(RealSubject)相同的接口。
  • 代理持有真实对象的引用,并在访问前后执行额外操作(如权限检查、延迟初始化、日志)。

结构(UML)

┌─────────────┐
│   Subject   │
├─────────────┤
│ + request() │
└─────────────┘
        △
   ┌────┴────┐
┌───────┐ ┌───────┐
│ Proxy │ │RealSubject│
├───────┤ ├───────┤
│ + request()│ │ + request()│
└───────┘ └───────┘

参与者

  • Subject:定义 RealSubject 和 Proxy 的公共接口。
  • RealSubject:真正的业务对象。
  • Proxy:维护引用使得可以访问 RealSubject,并实现 Subject 接口。

协作

  • 客户端通过 Proxy 访问 RealSubject,Proxy 控制访问方式(远程、虚拟、保护等)。

适用性

  • 远程代理:隐藏对象位于不同地址空间的事实。
  • 虚拟代理:延迟加载,直到真正需要才创建对象。
  • 保护代理:控制访问权限。
  • 智能引用:在访问时附加额外操作(如引用计数、日志)。

Python 示例

# Subject
class Image:
    def display(self): pass
 
# RealSubject
class RealImage(Image):
    def __init__(self, filename):
        self._filename = filename
        self._load_from_disk()
    def _load_from_disk(self):
        print(f"Loading {self._filename} from disk...")
    def display(self):
        print(f"Displaying {self._filename}")
 
# Proxy
class ProxyImage(Image):
    def __init__(self, filename):
        self._filename = filename
        self._real_image = None
    def display(self):
        if self._real_image is None:
            self._real_image = RealImage(self._filename)
        self._real_image.display()
 
# Client
img = ProxyImage("photo.jpg")
img.display()   # loads and displays
img.display()   # only displays (already loaded)

结构型模式总结

模式核心思想主要解决的问题
适配器转换接口,使不兼容的类可协作接口不匹配
桥接抽象与实现分离,独立变化多维度变化导致类爆炸
组合树形结构,统一对待叶子和容器部分-整体层次表示
装饰器动态添加职责,替代子类扩展避免子类膨胀,运行时增强
外观提供统一简化接口,封装子系统复杂子系统易用性
享元共享细粒度对象,节省内存大量相似对象的内存开销
代理控制对象访问,增加间接层访问控制、延迟加载、远程访问