设计模式之结构型设计模式详解

结构型模式关注如何将类或对象组合成更大、更灵活的结构。


目录

  1. 适配器模式 (Adapter)
  2. 桥接模式 (Bridge)
  3. 组合模式 (Composite)
  4. 装饰器模式 (Decorator)
  5. 外观模式 (Facade)
  6. 享元模式 (Flyweight)
  7. 代理模式 (Proxy)

1. 适配器模式 (Adapter)

定义

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

生活类比

  • 电源适配器:不同国家的插座标准不同(美标、欧标、国标),通过适配器可以让你的充电器在任何国家使用
  • 翻译官:两个说不同语言的人通过翻译官进行交流

结构

复制代码
Client -> Target <- Adapter <- Adaptee

Python 实现

复制代码
from abc import ABC, abstractmethod

class MediaPlayer(ABC):
    @abstractmethod
    def play(self, filename: str):
        pass

class AdvancedMediaPlayer(ABC):
    @abstractmethod
    def play_vlc(self, filename: str):
        pass

    @abstractmethod
    def play_mp4(self, filename: str):
        pass

class VlcPlayer(AdvancedMediaPlayer):
    def play_vlc(self, filename: str):
        print(f"Playing vlc file: {filename}")

    def play_mp4(self, filename: str):
        pass

class Mp4Player(AdvancedMediaPlayer):
    def play_vlc(self, filename: str):
        pass

    def play_mp4(self, filename: str):
        print(f"Playing mp4 file: {filename}")

class MediaAdapter(MediaPlayer):
    def __init__(self, audio_type: str):
        self.audio_type = audio_type
        if audio_type == "vlc":
            self.advanced_player = VlcPlayer()
        elif audio_type == "mp4":
            self.advanced_player = Mp4Player()

    def play(self, filename: str):
        if self.audio_type == "vlc":
            self.advanced_player.play_vlc(filename)
        elif self.audio_type == "mp4":
            self.advanced_player.play_mp4(filename)

class AudioPlayer(MediaPlayer):
    def __init__(self):
        self.adapter = None

    def play(self, audio_type: str, filename: str):
        if audio_type == "mp3":
            print(f"Playing mp3 file: {filename}")
        elif audio_type in ["vlc", "mp4"]:
            self.adapter = MediaAdapter(audio_type)
            self.adapter.play(filename)
        else:
            print(f"Invalid media. {audio_type} format not supported")

if __name__ == "__main__":
    player = AudioPlayer()
    player.play("mp3", "song.mp3")
    player.play("mp4", "movie.mp4")
    player.play("vlc", "video.vlc")
    player.play("avi", "clip.avi")

适用场景

  • 希望复用一些现存的类,但接口与复用环境不一致
  • 想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作

2. 桥接模式 (Bridge)

定义

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

生活类比

  • 遥控器和电视:遥控器(抽象)和电视机(实现)是分开的,你可以换不同品牌的电视,而不用换遥控器
  • 画笔和颜料:画笔是抽象,颜料是实现,可以任意组合

Python 实现

复制代码
from abc import ABC, abstractmethod

class Color(ABC):
    @abstractmethod
    def apply_color(self):
        pass

class RedColor(Color):
    def apply_color(self):
        return "红色"

class BlueColor(Color):
    def apply_color(self):
        return "蓝色"

class GreenColor(Color):
    def apply_color(self):
        return "绿色"

class Shape(ABC):
    def __init__(self, color: Color):
        self.color = color

    @abstractmethod
    def draw(self):
        pass

class Circle(Shape):
    def draw(self):
        print(f"画一个{self.color.apply_color()}的圆形")

class Square(Shape):
    def draw(self):
        print(f"画一个{self.color.apply_color()}的正方形")

class Triangle(Shape):
    def draw(self):
        print(f"画一个{self.color.apply_color()}的三角形")

if __name__ == "__main__":
    red_circle = Circle(RedColor())
    red_circle.draw()

    blue_square = Square(BlueColor())
    blue_square.draw()

    green_triangle = Triangle(GreenColor())
    green_triangle.draw()

适用场景

  • 不希望抽象和实现部分有一个固定的绑定关系
  • 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充

3. 组合模式 (Composite)

定义

将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

生活类比

  • 文件系统:文件夹可以包含文件,也可以包含其他文件夹,形成树形结构
  • 公司组织架构:CEO管理部门经理,部门经理管理员工,形成层级结构

Python 实现

复制代码
from abc import ABC, abstractmethod
from typing import List

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

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

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

class File(FileSystemComponent):
    def __init__(self, name: str, size: int):
        super().__init__(name)
        self.size = size

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

    def get_size(self) -> int:
        return self.size

class Folder(FileSystemComponent):
    def __init__(self, name: str):
        super().__init__(name)
        self.children: List[FileSystemComponent] = []

    def add(self, component: FileSystemComponent):
        self.children.append(component)

    def remove(self, component: FileSystemComponent):
        self.children.remove(component)

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

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

if __name__ == "__main__":
    root = Folder("root")

    file1 = File("readme.txt", 10)
    file2 = File("main.py", 50)
    root.add(file1)
    root.add(file2)

    sub_folder = Folder("src")
    file3 = File("utils.py", 20)
    file4 = File("config.py", 5)
    sub_folder.add(file3)
    sub_folder.add(file4)

    root.add(sub_folder)

    root.display()
    print(f"Total size: {root.get_size()}KB")

适用场景

  • 想表示对象的部分-整体层次结构
  • 希望用户忽略组合对象与单个对象的不同,统一使用所有对象

4. 装饰器模式 (Decorator)

定义

动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。

生活类比

  • 咖啡加料:基础咖啡可以加奶、加糖、加奶油,每种加料都会改变价格和描述
  • 穿衣服:基础装扮可以通过不同的服饰(外套、围巾、帽子)进行装饰

Python 实现

复制代码
from abc import ABC, abstractmethod

class Coffee(ABC):
    @abstractmethod
    def cost(self) -> float:
        pass

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

class SimpleCoffee(Coffee):
    def cost(self) -> float:
        return 10.0

    def description(self) -> str:
        return "简单咖啡"

class CoffeeDecorator(Coffee, ABC):
    def __init__(self, coffee: Coffee):
        self._coffee = coffee

    def cost(self) -> float:
        return self._coffee.cost()

    def description(self) -> str:
        return self._coffee.description()

class MilkDecorator(CoffeeDecorator):
    def cost(self) -> float:
        return self._coffee.cost() + 2.0

    def description(self) -> str:
        return self._coffee.description() + ", 牛奶"

class SugarDecorator(CoffeeDecorator):
    def cost(self) -> float:
        return self._coffee.cost() + 1.0

    def description(self) -> str:
        return self._coffee.description() + ", 糖"

class WhipDecorator(CoffeeDecorator):
    def cost(self) -> float:
        return self._coffee.cost() + 3.0

    def description(self) -> str:
        return self._coffee.description() + ", 奶油"

if __name__ == "__main__":
    coffee = SimpleCoffee()
    print(f"{coffee.description()}: ${coffee.cost()}")

    milk_coffee = MilkDecorator(SimpleCoffee())
    print(f"{milk_coffee.description()}: ${milk_coffee.cost()}")

    deluxe_coffee = WhipDecorator(MilkDecorator(SugarDecorator(SimpleCoffee())))
    print(f"{deluxe_coffee.description()}: ${deluxe_coffee.cost()}")

适用场景

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
  • 处理那些可以撤销的职责

5. 外观模式 (Facade)

定义

为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

生活类比

  • 餐厅服务员:你不需要知道厨房如何做菜、如何调配,只需要告诉服务员你想吃什么
  • 电脑开机按钮:按下按钮,内部电源、主板、硬盘等协同工作,你不需要了解细节

Python 实现

复制代码
class CPU:
    def freeze(self):
        print("CPU: freezing processor")

    def jump(self, position):
        print(f"CPU: jumping to address {position}")

    def execute(self):
        print("CPU: executing")

class Memory:
    def load(self, position, data):
        print(f"Memory: loading data from position {position}")

class HardDrive:
    def read(self, lba, size):
        print(f"HardDrive: reading {size} bytes from LBA {lba}")
        return f"boot_data_{lba}"

class ComputerFacade:
    def __init__(self):
        self.cpu = CPU()
        self.memory = Memory()
        self.hard_drive = HardDrive()

    def start(self):
        print("=== Starting Computer ===")
        self.cpu.freeze()
        boot_data = self.hard_drive.read(0, 1024)
        self.memory.load(0, boot_data)
        self.cpu.jump(0)
        self.cpu.execute()
        print("=== Computer Started ===")

    def shutdown(self):
        print("=== Shutting Down ===")
        print("Saving data...")
        print("Computer shut down")

if __name__ == "__main__":
    computer = ComputerFacade()
    computer.start()
    computer.shutdown()

适用场景

  • 为复杂的子系统提供一个简单的接口
  • 客户端与抽象类的实现部分之间存在很大的依赖性

6. 享元模式 (Flyweight)

定义

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

生活类比

  • 围棋:棋盘上有361个位置,但黑白棋子只有两种,不需要为每个位置创建新棋子对象
  • 图书馆:图书馆有成千上万本书,但书籍类型是有限的,可以共享类型信息

Python 实现

复制代码
from abc import ABC, abstractmethod
from typing import Dict

class CharacterFlyweight(ABC):
    @abstractmethod
    def display(self, position_x: int, position_y: int, font_size: int):
        pass

class Character(CharacterFlyweight):
    def __init__(self, symbol: str):
        self.symbol = symbol
        print(f"Creating character: '{symbol}'")

    def display(self, position_x: int, position_y: int, font_size: int):
        print(f"Character '{self.symbol}' at ({position_x}, {position_y}), size: {font_size}")

class CharacterFactory:
    _characters: Dict[str, Character] = {}

    @classmethod
    def get_character(cls, symbol: str) -> Character:
        if symbol not in cls._characters:
            cls._characters[symbol] = Character(symbol)
        return cls._characters[symbol]

    @classmethod
    def get_count(cls):
        return len(cls._characters)

if __name__ == "__main__":
    chars = "Hello World"

    for i, char in enumerate(chars):
        char_obj = CharacterFactory.get_character(char)
        char_obj.display(i * 10, 0, 12)

    print(f"Total chars: {len(chars)}")
    print(f"Objects created: {CharacterFactory.get_count()}")

适用场景

  • 一个应用程序使用了大量的对象
  • 由于使用大量对象,造成很大的存储开销
  • 对象的大多数状态都可变为外部状态

7. 代理模式 (Proxy)

定义

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

生活类比

  • 明星经纪人:粉丝不直接接触明星,而是通过经纪人安排见面
  • VPN/代理服务器:你通过代理访问被限制的网站,代理帮你转发请求
  • 信用卡:信用卡是现金的代理,你不需要携带大量现金

Python 实现

复制代码
from abc import ABC, abstractmethod
import time

class Image(ABC):
    @abstractmethod
    def display(self):
        pass

class RealImage(Image):
    def __init__(self, filename: str):
        self.filename = filename
        self.load_from_disk()

    def load_from_disk(self):
        print(f"Loading image: {filename} (heavy operation)")
        time.sleep(1)

    def display(self):
        print(f"Displaying image: {self.filename}")

class ProxyImage(Image):
    def __init__(self, filename: str):
        self.filename = filename
        self._real_image = None

    def display(self):
        if self._real_image is None:
            print("Proxy: creating real image on first access")
            self._real_image = RealImage(self.filename)
        else:
            print("Proxy: using cached image")
        self._real_image.display()

class Document(ABC):
    @abstractmethod
    def view(self):
        pass

    @abstractmethod
    def edit(self):
        pass

class RealDocument(Document):
    def __init__(self, content: str):
        self.content = content

    def view(self):
        print(f"Viewing document: {self.content}")

    def edit(self):
        print(f"Editing document: {self.content}")

class DocumentProxy(Document):
    def __init__(self, content: str, user_role: str):
        self.real_document = RealDocument(content)
        self.user_role = user_role

    def view(self):
        self.real_document.view()

    def edit(self):
        if self.user_role == "admin":
            self.real_document.edit()
        else:
            print(f"Access denied: role '{self.user_role}' cannot edit")

if __name__ == "__main__":
    # Virtual Proxy
    image = ProxyImage("photo.jpg")
    print("First display:")
    image.display()
    print("Second display:")
    image.display()

    # Protection Proxy
    user_doc = DocumentProxy("Secret content", "user")
    user_doc.view()
    user_doc.edit()

    admin_doc = DocumentProxy("Secret content", "admin")
    admin_doc.view()
    admin_doc.edit()

适用场景

  • 远程代理:为位于不同地址空间的对象提供本地代表
  • 虚拟代理:根据需要创建开销很大的对象
  • 保护代理:控制对原始对象的访问

总结对比

模式 核心思想 使用场景
适配器 接口转换 兼容不同接口的类
桥接 抽象与实现分离 多维度变化的类层次结构
组合 部分-整体层次 树形结构,统一处理个体和组合
装饰器 动态添加职责 在不修改原类的情况下扩展功能
外观 简化接口 为复杂子系统提供简单入口
享元 共享细粒度对象 大量相似对象,减少内存占用
代理 控制对象访问 延迟加载、访问控制、远程调用
相关推荐
gogogo出发喽2 小时前
flask vue
python
倒流时光三十年2 小时前
重学设计模式 之 流式 Builder 模式(Fluent Builder)
设计模式·流式 builder·fluent builder
斯班奇的好朋友阿法法2 小时前
Django 3.2 项目:从 Hello World 开始(完整功能版)
python·django
架构师老Y2 小时前
010:API网关调试手记:路由、认证与限流的那些坑
开发语言·前端·python
老刘说AI2 小时前
Dify:从入门到精通
人工智能·python·神经网络·低代码·ai作画·开源软件
IT枫斗者2 小时前
AI Agent 设计模式全景解析:从单体智能到分布式协作的架构演进
人工智能·redis·分布式·算法·spring·缓存·设计模式
zhangzeyuaaa2 小时前
Python 闭包详解
开发语言·python
万粉变现经纪人2 小时前
如何解决 pip install tensorflow-gpu 报错 未检测到 CUDA 驱动 问题
人工智能·python·深度学习·aigc·tensorflow·bug·pip
架构师老Y2 小时前
009、容器编排实战:Kubernetes上的Python服务
python·容器·kubernetes