结构型模式关注如何将类或对象组合成更大、更灵活的结构。
目录
- 适配器模式 (Adapter)
- 桥接模式 (Bridge)
- 组合模式 (Composite)
- 装饰器模式 (Decorator)
- 外观模式 (Facade)
- 享元模式 (Flyweight)
- 代理模式 (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()
适用场景
- 远程代理:为位于不同地址空间的对象提供本地代表
- 虚拟代理:根据需要创建开销很大的对象
- 保护代理:控制对原始对象的访问
总结对比
| 模式 | 核心思想 | 使用场景 |
|---|---|---|
| 适配器 | 接口转换 | 兼容不同接口的类 |
| 桥接 | 抽象与实现分离 | 多维度变化的类层次结构 |
| 组合 | 部分-整体层次 | 树形结构,统一处理个体和组合 |
| 装饰器 | 动态添加职责 | 在不修改原类的情况下扩展功能 |
| 外观 | 简化接口 | 为复杂子系统提供简单入口 |
| 享元 | 共享细粒度对象 | 大量相似对象,减少内存占用 |
| 代理 | 控制对象访问 | 延迟加载、访问控制、远程调用 |