跳转至

03-结构型模式

结构型模式关系图

学习时间: 约4-5小时 难度级别: ⭐⭐⭐ 中级 前置知识: SOLID设计原则、创建型模式 学习目标: 掌握7种结构型模式的意图、结构和实现,理解如何通过组合对象获得新功能


🎯 学习目标

  1. 理解结构型模式解决的核心问题:如何将类或对象组合成更大的结构
  2. 掌握适配器、装饰器、代理三大高频结构型模式
  3. 理解Python装饰器与装饰器模式的关系
  4. 掌握代理模式的多种应用(缓存代理、远程代理、保护代理)
  5. 能在实际项目中识别并应用结构型模式

目录


1. 结构型模式概述

结构型模式关注如何将类和对象组合为更大的结构,在保持结构灵活高效的同时实现新功能。

模式 一句话 核心思想
适配器 接口转换器 不兼容接口之间的桥梁
桥接 分离抽象与实现 多维度变化独立扩展
组合 树形结构 部分-整体统一处理
装饰器 动态包装 不修改对象动态添加功能
外观 简化接口 复杂子系统的统一简单入口
享元 共享对象 大量相似对象共享内部状态
代理 控制访问 通过代理间接访问真实对象

2. 适配器模式(Adapter)

意图

将一个类的接口转换为客户端期望的另一个接口,使原本不兼容的类可以一起工作。

适用场景

  • 使用已有类,但接口与需求不匹配
  • 集成第三方库,统一不同SDK的接口
  • 旧系统迁移,新旧接口不兼容

结构

Text Only
Client → Target接口 → Adapter → Adaptee(被适配的类)

Python实现

Python
from abc import ABC, abstractmethod

# ====== 目标接口 ======
class PaymentGateway(ABC):
    @abstractmethod
    def pay(self, amount: float) -> bool:
        pass

# ====== 已有的第三方SDK(接口不同) ======
class AlipaySDK:
    def create_payment(self, total_fee, subject=""):
        print(f"Alipay: ¥{total_fee}")
        return {"status": "success"}

class WeChatPaySDK:
    def unified_order(self, total_amount_in_cents):
        print(f"WeChatPay: ¥{total_amount_in_cents / 100}")
        return {"return_code": "SUCCESS"}

class PayPalSDK:
    def make_payment(self, usd_amount):
        print(f"PayPal: ${usd_amount}")
        return True

# ====== 适配器 ======
class AlipayAdapter(PaymentGateway):
    def __init__(self, sdk: AlipaySDK):
        self._sdk = sdk

    def pay(self, amount):
        result = self._sdk.create_payment(total_fee=amount)
        return result.get("status") == "success"

class WeChatPayAdapter(PaymentGateway):
    def __init__(self, sdk: WeChatPaySDK):
        self._sdk = sdk

    def pay(self, amount):
        result = self._sdk.unified_order(total_amount_in_cents=int(amount * 100))
        return result.get("return_code") == "SUCCESS"

class PayPalAdapter(PaymentGateway):
    def __init__(self, sdk: PayPalSDK):
        self._sdk = sdk

    def pay(self, amount):
        return self._sdk.make_payment(usd_amount=amount)

# ====== 使用 ======
def checkout(gateway: PaymentGateway, amount: float):
    """客户端代码只依赖统一接口"""
    if gateway.pay(amount):
        print("Payment successful!")
    else:
        print("Payment failed!")

checkout(AlipayAdapter(AlipaySDK()), 99.9)
checkout(WeChatPayAdapter(WeChatPaySDK()), 99.9)

Java实现

Java
public interface PaymentGateway {
    boolean pay(double amount);
}

public class AlipayAdapter implements PaymentGateway {
    private AlipaySDK sdk;

    public AlipayAdapter(AlipaySDK sdk) { this.sdk = sdk; }

    public boolean pay(double amount) {
        Map<String, String> result = sdk.createPayment(amount, "");
        return "success".equals(result.get("status"));
    }
}

优缺点

优点 缺点
解耦客户端与第三方实现 增加了间接层的复杂度
遵循开闭原则 适配器过多时代码变复杂
单一职责(接口转换独立)

实际应用

  • Python的 io.TextIOWrapper 适配字节流为文本流
  • Java JDBC驱动适配不同数据库
  • 旧API到新API的迁移层

3. 桥接模式(Bridge)

意图

抽象实现分离,使两者可以独立变化。处理两个维度的变化。

适用场景

  • 一个类有两个或多个独立变化的维度
  • 避免多维继承导致的类爆炸

Python实现

Python
from abc import ABC, abstractmethod

# ====== 实现维度:渲染方式 ======
class Renderer(ABC):
    @abstractmethod
    def render_circle(self, x, y, radius): pass

    @abstractmethod
    def render_rectangle(self, x, y, w, h): pass

class VectorRenderer(Renderer):
    def render_circle(self, x, y, radius):
        print(f"Drawing circle at ({x},{y}) r={radius} as vector")

    def render_rectangle(self, x, y, w, h):
        print(f"Drawing rect at ({x},{y}) {w}x{h} as vector")

class RasterRenderer(Renderer):
    def render_circle(self, x, y, radius):
        print(f"Drawing circle at ({x},{y}) r={radius} as pixels")

    def render_rectangle(self, x, y, w, h):
        print(f"Drawing rect at ({x},{y}) {w}x{h} as pixels")

# ====== 抽象维度:图形类型 ======
class Shape(ABC):
    def __init__(self, renderer: Renderer):
        self.renderer = renderer  # 桥接:持有渲染器引用

    @abstractmethod
    def draw(self): pass

class Circle(Shape):
    def __init__(self, renderer, x, y, radius):
        super().__init__(renderer)
        self.x, self.y, self.radius = x, y, radius

    def draw(self):
        self.renderer.render_circle(self.x, self.y, self.radius)

class Rectangle(Shape):
    def __init__(self, renderer, x, y, w, h):
        super().__init__(renderer)
        self.x, self.y, self.w, self.h = x, y, w, h

    def draw(self):
        self.renderer.render_rectangle(self.x, self.y, self.w, self.h)

# 两个维度可以自由组合
circle_vector = Circle(VectorRenderer(), 10, 10, 5)
circle_raster = Circle(RasterRenderer(), 10, 10, 5)
circle_vector.draw()
circle_raster.draw()

优缺点

优点 缺点
分离抽象与实现,独立扩展 增加了代码复杂度
避免继承层次爆炸 需要正确识别两个维度
符合开闭原则

4. 组合模式(Composite)

意图

将对象组合为树形结构,使客户端可以统一处理叶子节点和组合节点。

Python实现

Python
from abc import ABC, abstractmethod
from typing import List

class FileSystemItem(ABC):
    def __init__(self, name):
        self.name = name

    @abstractmethod
    def get_size(self) -> int:
        pass

    @abstractmethod
    def display(self, indent=0):
        pass

class File(FileSystemItem):
    """叶子节点"""
    def __init__(self, name, size):
        super().__init__(name)
        self.size = size

    def get_size(self):
        return self.size

    def display(self, indent=0):
        print("  " * indent + f"📄 {self.name} ({self.size}KB)")

class Directory(FileSystemItem):
    """组合节点"""
    def __init__(self, name):
        super().__init__(name)
        self.children: List[FileSystemItem] = []

    def add(self, item: FileSystemItem):
        self.children.append(item)
        return self

    def remove(self, item: FileSystemItem):
        self.children.remove(item)

    def get_size(self):
        return sum(child.get_size() for child in self.children)

    def display(self, indent=0):
        print("  " * indent + f"📁 {self.name}/ ({self.get_size()}KB)")
        for child in self.children:
            child.display(indent + 1)

# 构建树形结构
root = Directory("project")
src = Directory("src")
src.add(File("main.py", 15))
src.add(File("utils.py", 8))
root.add(src)
root.add(File("README.md", 3))
root.add(File("requirements.txt", 1))

root.display()
# 📁 project/ (27KB)
#   📁 src/ (23KB)
#     📄 main.py (15KB)
#     📄 utils.py (8KB)
#   📄 README.md (3KB)
#   📄 requirements.txt (1KB)

实际应用

  • 文件系统(文件和目录)
  • GUI组件树(容器和控件)
  • 组织架构(部门和员工)
  • HTML DOM树

5. 装饰器模式(Decorator)

意图

动态地为对象添加新功能,比继承更灵活。

适用场景

  • 需要在运行时动态扩展对象功能
  • 不想通过继承产生大量子类
  • 需要灵活组合多种增强功能

Python实现

Python
from abc import ABC, abstractmethod

# ====== 组件接口 ======
class DataSource(ABC):
    @abstractmethod
    def write(self, data: str): pass

    @abstractmethod
    def read(self) -> str: pass

# ====== 具体组件 ======
class FileDataSource(DataSource):
    def __init__(self, filename):
        self.filename = filename
        self._data = ""

    def write(self, data):
        self._data = data
        print(f"Writing to {self.filename}: {data[:50]}...")  # 切片操作:[start:end:step]提取子序列

    def read(self):
        return self._data

# ====== 装饰器基类 ======
class DataSourceDecorator(DataSource):
    def __init__(self, source: DataSource):
        self._source = source

    def write(self, data):
        self._source.write(data)

    def read(self):
        return self._source.read()

# ====== 具体装饰器 ======
class EncryptionDecorator(DataSourceDecorator):
    """加密装饰器"""
    def write(self, data):
        encrypted = self._encrypt(data)
        super().write(encrypted)  # super()调用父类方法

    def read(self):
        data = super().read()
        return self._decrypt(data)

    def _encrypt(self, data):
        return ''.join(chr(ord(c) + 1) for c in data)

    def _decrypt(self, data):
        return ''.join(chr(ord(c) - 1) for c in data)

class CompressionDecorator(DataSourceDecorator):
    """压缩装饰器"""
    def write(self, data):
        compressed = self._compress(data)
        super().write(compressed)

    def read(self):
        data = super().read()
        return self._decompress(data)

    def _compress(self, data):
        return f"[compressed:{len(data)}]{data[:10]}..."

    def _decompress(self, data):
        return data  # 简化实现

class LoggingDecorator(DataSourceDecorator):
    """日志装饰器"""
    def write(self, data):
        print(f"[LOG] Writing {len(data)} bytes")
        super().write(data)

    def read(self):
        print(f"[LOG] Reading data")
        return super().read()

# ====== 灵活组合 ======
# 基础写入
source = FileDataSource("data.txt")

# 加密 + 压缩 + 日志(像套娃一样层层包装)
source = LoggingDecorator(
    CompressionDecorator(
        EncryptionDecorator(source)
    )
)

source.write("Hello, Design Patterns!")
data = source.read()

与Python装饰器的关系

Python的 @decorator 语法糖与设计模式中的装饰器模式思想相似但不完全等同

  • 设计模式的装饰器:面向对象,包装一个对象,增强其方法
  • Python的 @decorator:包装一个函数,增强其行为
Python
import functools
import time

# Python函数装饰器 — 装饰器模式的函数式实现
def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):  # *args接收任意位置参数;**kwargs接收任意关键字参数
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

def retry(max_attempts=3):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:  # try/except捕获异常
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    print(f"Retry {attempt + 1}/{max_attempts}: {e}")
        return wrapper
    return decorator

@timer
@retry(max_attempts=3)
def fetch_data(url):
    # 模拟网络请求
    return f"Data from {url}"

Java实现

Java
public interface DataSource {  // interface定义类型契约
    void write(String data);
    String read();
}

public class EncryptionDecorator implements DataSource {
    private DataSource source;

    public EncryptionDecorator(DataSource source) {
        this.source = source;
    }

    public void write(String data) {
        source.write(encrypt(data));
    }

    public String read() {
        return decrypt(source.read());
    }
}

// 组合使用
DataSource source = new LoggingDecorator(
    new EncryptionDecorator(
        new FileDataSource("data.txt")
    )
);

优缺点

优点 缺点
比继承更灵活 多层装饰器调试困难
运行时动态添加功能 装饰器顺序可能影响结果
符合开闭原则 包装层过多影响性能(通常可忽略)

实际应用

  • Java的 BufferedReader(new FileReader(file)) — I/O流装饰器
  • Python的 @property, @staticmethod, @classmethod
  • Django/Flask的视图装饰器(@login_required

6. 外观模式(Facade)

意图

为复杂子系统提供一个简单的统一接口

Python实现

Python
class VideoDecoder:
    def decode(self, filename):
        print(f"Decoding video: {filename}")
        return "video_data"

class AudioDecoder:
    def decode(self, filename):
        print(f"Decoding audio: {filename}")
        return "audio_data"

class SubtitleParser:
    def parse(self, filename):
        print(f"Parsing subtitles: {filename}")
        return "subtitle_data"

class VideoRenderer:
    def render(self, video, audio, subtitles):
        print(f"Rendering video with audio and subtitles")

# 外观类:简化复杂操作
class VideoPlayerFacade:
    def __init__(self):
        self._video_decoder = VideoDecoder()
        self._audio_decoder = AudioDecoder()
        self._subtitle_parser = SubtitleParser()
        self._renderer = VideoRenderer()

    def play(self, filename):
        """一个方法搞定复杂的播放流程"""
        video = self._video_decoder.decode(filename)
        audio = self._audio_decoder.decode(filename)
        subtitles = self._subtitle_parser.parse(filename)
        self._renderer.render(video, audio, subtitles)

# 使用:简单调用
player = VideoPlayerFacade()
player.play("movie.mp4")

实际应用

  • Python的 requests 库是HTTP协议的外观
  • jQuery简化了DOM操作API
  • SLF4J是多种日志框架的统一外观

7. 享元模式(Flyweight)

意图

通过共享细粒度对象来减少内存用量,适用于大量相似对象的场景。

Python实现

Python
class TreeType:
    """享元对象:共享的内部状态"""
    def __init__(self, name, color, texture):
        self.name = name
        self.color = color
        self.texture = texture

    def draw(self, x, y):
        print(f"Drawing {self.name} tree at ({x},{y})")

class TreeFactory:
    """享元工厂:管理共享对象"""
    _types = {}

    @classmethod  # @classmethod类方法,第一个参数为类本身
    def get_tree_type(cls, name, color, texture) -> TreeType:
        key = f"{name}_{color}_{texture}"
        if key not in cls._types:
            cls._types[key] = TreeType(name, color, texture)
            print(f"  [New TreeType created: {name}]")
        return cls._types[key]

class Tree:
    """包含外部状态(位置)的具体树"""
    def __init__(self, x, y, tree_type: TreeType):
        self.x = x
        self.y = y
        self.type = tree_type  # 引用共享的享元

    def draw(self):
        self.type.draw(self.x, self.y)

# 创建森林:100万棵树,但只有少数几种类型
import random

forest = []
for _ in range(10):
    tree_type = TreeFactory.get_tree_type(
        random.choice(["Oak", "Pine", "Birch"]),
        random.choice(["Green", "DarkGreen"]),
        "standard"
    )
    forest.append(Tree(random.randint(0, 1000), random.randint(0, 1000), tree_type))

print(f"\nTrees: {len(forest)}, Shared types: {len(TreeFactory._types)}")

实际应用

  • Python的小整数缓存(-5到256)
  • Java的String常量池
  • 字体渲染中的字形缓存
  • 游戏中大量相同贴图的渲染

8. 代理模式(Proxy)

意图

为对象提供一个替代品占位符,以控制对该对象的访问。

代理类型

类型 说明 典型场景
虚拟代理 延迟创建昂贵对象 大图片懒加载
保护代理 控制访问权限 权限检查
远程代理 代表远程对象 RPC / REST API客户端
缓存代理 缓存请求结果 API缓存、数据库查询缓存
日志代理 记录访问日志 API调用记录

Python实现

Python
from abc import ABC, abstractmethod  # ABC抽象基类;abstractmethod强制子类实现
import time
from functools import lru_cache

# ====== 接口 ======
class DataService(ABC):
    @abstractmethod
    def get_data(self, key: str) -> str:
        pass

# ====== 真实服务 ======
class RemoteDataService(DataService):
    def get_data(self, key):
        print(f"  [Remote] Fetching data for '{key}'... (slow)")
        time.sleep(0.5)  # 模拟网络延迟
        return f"Data-{key}-{time.time():.0f}"

# ====== 缓存代理 ======
class CachingProxy(DataService):
    def __init__(self, service: DataService, ttl=60):
        self._service = service
        self._cache = {}
        self._ttl = ttl

    def get_data(self, key):
        now = time.time()
        if key in self._cache:
            data, timestamp = self._cache[key]
            if now - timestamp < self._ttl:
                print(f"  [Cache HIT] '{key}'")
                return data

        print(f"  [Cache MISS] '{key}'")
        data = self._service.get_data(key)
        self._cache[key] = (data, now)
        return data

# ====== 保护代理 ======
class AuthProxy(DataService):
    def __init__(self, service: DataService, allowed_roles: list):
        self._service = service
        self._allowed_roles = allowed_roles
        self._current_role = "guest"

    def login(self, role):
        self._current_role = role

    def get_data(self, key):
        if self._current_role not in self._allowed_roles:
            raise PermissionError(f"Role '{self._current_role}' not allowed!")
        return self._service.get_data(key)

# ====== 日志代理 ======
class LoggingProxy(DataService):
    def __init__(self, service: DataService):
        self._service = service

    def get_data(self, key):
        print(f"[LOG] get_data('{key}') called at {time.strftime('%H:%M:%S')}")
        start = time.time()
        result = self._service.get_data(key)
        elapsed = time.time() - start
        print(f"[LOG] get_data('{key}') returned in {elapsed:.3f}s")
        return result

# ====== 组合使用 ======
service = LoggingProxy(
    CachingProxy(
        RemoteDataService(),
        ttl=10
    )
)

print(service.get_data("user:1"))  # Cache MISS → 远程调用
print(service.get_data("user:1"))  # Cache HIT → 直接返回

Java实现(动态代理)

Java
import java.lang.reflect.*;

// JDK动态代理
public class LoggingHandler implements InvocationHandler {  // extends继承;implements实现接口
    private Object target;

    public LoggingHandler(Object target) {
        this.target = target;
    }

    @Override  // @Override重写父类方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[LOG] Calling: " + method.getName());
        long start = System.currentTimeMillis();
        Object result = method.invoke(target, args);
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("[LOG] " + method.getName() + " took " + elapsed + "ms");
        return result;
    }
}

// 创建动态代理
DataService realService = new RemoteDataService();
DataService proxy = (DataService) Proxy.newProxyInstance(
    DataService.class.getClassLoader(),
    new Class[]{DataService.class},
    new LoggingHandler(realService)
);
proxy.getData("user:1");

优缺点

优点 缺点
不修改真实对象,扩展功能 增加间接层,可能影响性能
可以控制对象生命周期 代码复杂度增加
对客户端透明 代理过多时难以维护

实际应用

  • Python的 @property 实质上是属性代理
  • ORM中的懒加载(如SQLAlchemy的延迟加载关系)
  • Web框架的中间件(缓存、认证、限流)
  • Java Spring AOP(基于动态代理)

9. 结构型模式对比

模式 核心 关系
适配器 接口转换 通常包装一个已存在的对象
桥接 分离两个维度 组合关系,构建时确定
组合 树形结构 部分-整体的递归结构
装饰器 动态增强 透明包装,可嵌套
外观 简化接口 不增加功能,只简化使用
享元 共享实例 分离内外状态
代理 控制访问 接口不变,增加控制

容易混淆的模式对比

  • 装饰器 vs 代理:装饰器增强功能(客户端知道),代理控制访问(客户端不知道内部有代理)
  • 适配器 vs 外观:适配器转换接口(一对一),外观简化接口(一对多子系统)
  • 装饰器 vs 适配器:装饰器不改变接口只增强功能,适配器改变接口

10. 练习与自我检查

✏️ 练习题

  1. 适配器实践:写一个适配器将 xml.etree.ElementTreejson 模块统一到相同的数据读取接口 DataReader.read(filepath) -> dict

  2. 装饰器组合:实现一个HTTP请求处理链:LoggingDecorator → AuthDecorator → RateLimitDecorator → RealHandler。验证装饰器顺序不同时行为的变化。

  3. 代理实现:为一个图片加载器实现虚拟代理(懒加载)— 只有在第一次调用 display() 时才从磁盘加载图片。

  4. 组合模式:用组合模式实现一个菜单系统,支持菜单项(叶子)和子菜单(组合),实现 display()get_total_price()

  5. 享元实践:模拟一个文字编辑器,大量字符共享字体/大小/颜色信息,每个字符只存储自己的位置。统计共享前后内存使用差异。

面试要点

Q1: 装饰器模式和代理模式的区别? A: 装饰器重在"增强功能"(如添加加密、压缩),客户端通常知道包了装饰器。代理重在"控制访问"(如缓存、权限验证),对客户端透明,接口不变。

Q2: Python的装饰器语法是装饰器模式吗? A: 思想类似但不完全等同。设计模式的装饰器是面向对象的(包装对象),Python的 @decorator 是函数式的(包装函数),但核心思想相同——不修改原始代码动态添加行为。

Q3: 适配器模式有哪些实际应用? A: 统一不同支付SDK接口、使用旧接口适配新系统、将XML数据源适配为JSON接口等。核心是在不修改已有类的前提下让不兼容的接口协同工作。

自我检查清单

  • 能区分适配器、装饰器和代理三者的意图差异
  • 能实现Python函数装饰器和类装饰器模式
  • 理解代理模式的多种变体(缓存/保护/虚拟/远程)
  • 能用组合模式处理树形结构
  • 理解外观模式的简化作用
  • 知道享元模式适用的场景(大量相似对象)

下一章: 04-行为型模式 — 学习对象之间如何高效协作