Python 常见的设计模型:入门到精通

设计模式是软件工程中经过验证的、可复用的解决方案,用于解决特定上下文中反复出现的设计问题。掌握设计模式能帮助你编写更优雅、可维护、可扩展的代码。本文将以Python为例,从基础概念到高级应用,详细讲解15种常用设计模式。每种模式均包含定义问题场景解决方案代码示例(带输出)优缺点适用场景。所有代码均可复制到本地运行,助你彻底理解设计模式的精髓。


目录

  1. 设计模式简介

  2. 创建型模式

    • 单例模式(Singleton)

    • 工厂方法模式(Factory Method)

    • 抽象工厂模式(Abstract Factory)

    • 建造者模式(Builder)

  3. 结构型模式

    • 适配器模式(Adapter)

    • 装饰器模式(Decorator)

    • 代理模式(Proxy)

    • 外观模式(Facade)

  4. 行为型模式

    • 观察者模式(Observer)

    • 策略模式(Strategy)

    • 模板方法模式(Template Method)

    • 状态模式(State)

    • 责任链模式(Chain of Responsibility)

  5. 总结与下载说明


1. 设计模式简介

设计模式主要分为三大类:

  • 创建型模式:对象实例化机制,如单例、工厂等。

  • 结构型模式:组合类或对象以形成更大结构,如适配器、装饰器等。

  • 行为型模式:对象之间的职责分配和通信,如观察者、策略等。

Python作为动态语言,部分模式实现更简洁(例如装饰器内置支持),但理解其背后的思想依然至关重要。


2. 创建型模式

2.1 单例模式(Singleton)

定义:确保一个类只有一个实例,并提供全局访问点。

问题场景:配置文件管理、数据库连接池、日志记录器等对象只需一个全局实例时。

解决方案 :通过重写__new__方法或使用元类控制实例创建。

代码示例

bash 复制代码
class Singleton:
    """经典单例实现"""
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            print("创建第一个实例")
            cls._instance = super().__new__(cls)
        else:
            print("返回已存在的实例")
        return cls._instance

    def __init__(self, value=None):
        # 注意:__init__每次都会执行,需要控制初始化逻辑
        if not hasattr(self, 'initialized'):
            self.value = value
            self.initialized = True

# 使用装饰器实现单例(更Pythonic)
def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class Logger:
    def __init__(self, level="INFO"):
        self.level = level
        print(f"日志级别设置为 {self.level}")

# 测试
if __name__ == "__main__":
    print("=== 单例模式测试 ===")
    s1 = Singleton("第一次")
    s2 = Singleton("第二次")
    print(f"s1 is s2: {s1 is s2}")  # True
    print(f"s1.value = {s1.value}, s2.value = {s2.value}")  # 第二次, 注意初始化被覆盖

    print("\n--- 装饰器单例 ---")
    log1 = Logger("DEBUG")
    log2 = Logger("ERROR")
    print(f"log1 is log2: {log1 is log2}")
    print(f"log1.level = {log1.level}, log2.level = {log2.level}")

输出

bash 复制代码
=== 单例模式测试 ===
创建第一个实例
返回已存在的实例
s1 is s2: True
s1.value = 第二次, s2.value = 第二次

--- 装饰器单例 ---
日志级别设置为 DEBUG
log1 is log2: True
log1.level = DEBUG, log2.level = DEBUG

优缺点

  • 优点:节省资源,全局唯一访问点。

  • 缺点:在多线程环境需要加锁(Python可用threading.Lock);可能隐藏依赖关系。

适用场景:配置类、连接池、线程池、注册表等。


2.2 工厂方法模式(Factory Method)

定义:定义一个创建对象的接口,但由子类决定实例化哪个类。将实例化延迟到子类。

问题场景:客户端不知道需要创建的具体对象类型,或者希望解耦对象创建与使用。

解决方案:创建抽象工厂类,具体工厂重写工厂方法。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 产品抽象
class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "汪汪"

class Cat(Animal):
    def speak(self):
        return "喵喵"

# 工厂抽象
class AnimalFactory(ABC):
    @abstractmethod
    def create_animal(self) -> Animal:
        pass

class DogFactory(AnimalFactory):
    def create_animal(self) -> Animal:
        return Dog()

class CatFactory(AnimalFactory):
    def create_animal(self) -> Animal:
        return Cat()

# 客户端
def animal_sound(factory: AnimalFactory):
    animal = factory.create_animal()
    print(animal.speak())

if __name__ == "__main__":
    print("=== 工厂方法模式 ===")
    animal_sound(DogFactory())
    animal_sound(CatFactory())

输出

进阶示例:参数化工厂方法

bash 复制代码
class Shape(ABC):
    @abstractmethod
    def draw(self):
        pass

class Circle(Shape):
    def draw(self):
        return "绘制圆形"

class Square(Shape):
    def draw(self):
        return "绘制方形"

class ShapeFactory:
    def create_shape(self, shape_type):
        if shape_type == "circle":
            return Circle()
        elif shape_type == "square":
            return Square()
        else:
            raise ValueError("未知形状")

if __name__ == "__main__":
    factory = ShapeFactory()
    shapes = ["circle", "square", "circle"]
    for s in shapes:
        shape = factory.create_shape(s)
        print(shape.draw())

输出

优缺点

  • 优点:符合开闭原则,增加新产品只需增加对应工厂类。

  • 缺点:每增加一种产品需要增加具体工厂类,系统复杂度增加。

适用场景:框架库中提供接口让用户扩展;创建逻辑复杂且经常变化。


2.3 抽象工厂模式(Abstract Factory)

定义:创建一系列相关或相互依赖对象的接口,无需指定具体类。

问题场景:系统需要生产多种产品族(如不同风格的UI组件:按钮、复选框),且产品之间搭配使用。

解决方案:定义抽象工厂,每个具体工厂生产一整套产品。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 抽象产品 A
class Button(ABC):
    @abstractmethod
    def paint(self):
        pass

# 抽象产品 B
class Checkbox(ABC):
    @abstractmethod
    def check(self):
        pass

# 具体产品族1:Windows风格
class WinButton(Button):
    def paint(self):
        return "Win按钮绘制"

class WinCheckbox(Checkbox):
    def check(self):
        return "Win复选框勾选"

# 具体产品族2:Mac风格
class MacButton(Button):
    def paint(self):
        return "Mac按钮绘制"

class MacCheckbox(Checkbox):
    def check(self):
        return "Mac复选框勾选"

# 抽象工厂
class GUIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button:
        pass
    @abstractmethod
    def create_checkbox(self) -> Checkbox:
        pass

# 具体工厂
class WinFactory(GUIFactory):
    def create_button(self) -> Button:
        return WinButton()
    def create_checkbox(self) -> Checkbox:
        return WinCheckbox()

class MacFactory(GUIFactory):
    def create_button(self) -> Button:
        return MacButton()
    def create_checkbox(self) -> Checkbox:
        return MacCheckbox()

# 客户端
def create_ui(factory: GUIFactory):
    button = factory.create_button()
    checkbox = factory.create_checkbox()
    print(button.paint())
    print(checkbox.check())

if __name__ == "__main__":
    print("=== 抽象工厂模式(Windows)===")
    create_ui(WinFactory())
    print("\n=== 抽象工厂模式(Mac)===")
    create_ui(MacFactory())

输出

bash 复制代码
=== 抽象工厂模式(Windows)===
Win按钮绘制
Win复选框勾选

=== 抽象工厂模式(Mac)===
Mac按钮绘制
Mac复选框勾选

优缺点

  • 优点:保证产品族内一致性;易于交换产品系列。

  • 缺点:增加新产品族困难(需修改抽象工厂接口);扩展产品等级较麻烦。

适用场景:界面工具包、游戏风格切换、跨平台开发。


2.4 建造者模式(Builder)

定义:将复杂对象的构建与其表示分离,使同样的构建过程可以创建不同的表示。

问题场景:创建包含多个可选部分的对象(如电脑:CPU、内存、硬盘、显卡),构造函数参数过多且混乱。

解决方案:使用Director控制构建流程,Builder负责具体部件的装配。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 产品
class Computer:
    def __init__(self):
        self.cpu = None
        self.ram = None
        self.storage = None
        self.gpu = None

    def __str__(self):
        return f"Computer [CPU={self.cpu}, RAM={self.ram}, Storage={self.storage}, GPU={self.gpu}]"

# 抽象建造者
class ComputerBuilder(ABC):
    @abstractmethod
    def build_cpu(self):
        pass
    @abstractmethod
    def build_ram(self):
        pass
    @abstractmethod
    def build_storage(self):
        pass
    @abstractmethod
    def build_gpu(self):
        pass
    @abstractmethod
    def get_result(self) -> Computer:
        pass

# 具体建造者:游戏电脑
class GamingComputerBuilder(ComputerBuilder):
    def __init__(self):
        self.computer = Computer()
    def build_cpu(self):
        self.computer.cpu = "Intel i9"
    def build_ram(self):
        self.computer.ram = "32GB DDR5"
    def build_storage(self):
        self.computer.storage = "1TB NVMe SSD"
    def build_gpu(self):
        self.computer.gpu = "NVIDIA RTX 4090"
    def get_result(self) -> Computer:
        return self.computer

# 具体建造者:办公电脑
class OfficeComputerBuilder(ComputerBuilder):
    def __init__(self):
        self.computer = Computer()
    def build_cpu(self):
        self.computer.cpu = "Intel i5"
    def build_ram(self):
        self.computer.ram = "16GB DDR4"
    def build_storage(self):
        self.computer.storage = "512GB SSD"
    def build_gpu(self):
        self.computer.gpu = "集成显卡"
    def get_result(self) -> Computer:
        return self.computer

# 指挥者
class Director:
    def __init__(self, builder: ComputerBuilder):
        self.builder = builder
    def construct_computer(self):
        self.builder.build_cpu()
        self.builder.build_ram()
        self.builder.build_storage()
        self.builder.build_gpu()
        return self.builder.get_result()

if __name__ == "__main__":
    print("=== 建造者模式 ===")
    gaming_builder = GamingComputerBuilder()
    director = Director(gaming_builder)
    gaming_pc = director.construct_computer()
    print(f"游戏电脑配置: {gaming_pc}")

    office_builder = OfficeComputerBuilder()
    director.builder = office_builder
    office_pc = director.construct_computer()
    print(f"办公电脑配置: {office_pc}")

输出

bash 复制代码
=== 建造者模式 ===
游戏电脑配置: Computer [CPU=Intel i9, RAM=32GB DDR5, Storage=1TB NVMe SSD, GPU=NVIDIA RTX 4090]
办公电脑配置: Computer [CPU=Intel i5, RAM=16GB DDR4, Storage=512GB SSD, GPU=集成显卡]

优缺点

  • 优点:分步构建,精细控制;复用同一构建流程创建不同表示。

  • 缺点:增加代码复杂度,需要多个类。

适用场景:复杂对象参数众多(尤其是可选参数)、配置对象、文档生成器(如PDF/HTML生成)。


3. 结构型模式

3.1 适配器模式(Adapter)

定义:将一个类的接口转换成客户希望的另一个接口,使原本因接口不兼容的类可以一起工作。

问题场景:现有类的方法签名与目标接口不符,无法直接使用。

解决方案:创建适配器类包装旧接口,转换调用。

代码示例

bash 复制代码
# 已有类(Adaptee)
class EuropeanSocket:
    def voltage(self):
        return 230
    def plug_type(self):
        return "圆形插头"

# 目标接口(Target)
class USASocket:
    def voltage(self):
        return 120
    def plug_type(self):
        return "扁平插头"

# 适配器:将欧式插座转为美式接口
class SocketAdapter(USASocket):
    def __init__(self, european_socket: EuropeanSocket):
        self.european_socket = european_socket
    def voltage(self):
        # 转换电压 230 -> 120(实际需要变压器,这里模拟)
        return 120
    def plug_type(self):
        # 转换插头形状
        return "扁平插头(适配)"

# 客户端只能使用USASocket
def charge_laptop(socket: USASocket):
    print(f"充电电压: {socket.voltage()}V, 插头类型: {socket.plug_type()}")

if __name__ == "__main__":
    print("=== 适配器模式 ===")
    eu_socket = EuropeanSocket()
    print(f"直接使用欧式插座: 电压{eu_socket.voltage()}V, {eu_socket.plug_type()}")

    # 使用适配器
    adapter = SocketAdapter(eu_socket)
    charge_laptop(adapter)

输出

bash 复制代码
=== 适配器模式 ===
直接使用欧式插座: 电压230V, 圆形插头
充电电压: 120V, 插头类型: 扁平插头(适配)

进阶:类适配器(多重继承)

bash 复制代码
class EuropeanVoltage:
    def get_voltage(self):
        return 230

class USASocketInterface:
    def supply_power(self):
        return 120

class Adapter(EuropeanVoltage, USASocketInterface):
    def supply_power(self):
        # 转换逻辑
        return 120

if __name__ == "__main__":
    adapter = Adapter()
    print(f"适配器供电: {adapter.supply_power()}V")

优缺点

  • 优点:提高类的复用性;透明性(客户端只看到目标接口)。

  • 缺点:过多适配器使系统复杂。

适用场景:集成第三方库、遗留系统改造、不同数据格式转换。


3.2 装饰器模式(Decorator)

定义:动态地给一个对象添加额外的职责,比继承更灵活。

问题场景:需要扩展一个类的功能,但又不想通过子类爆炸式增长。

解决方案:装饰器包装原有对象,在调用前后添加行为。

代码示例

bash 复制代码
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 5.0
    def description(self) -> str:
        return "基础咖啡"

# 装饰器基类
class CoffeeDecorator(Coffee):
    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() + 1.5
    def description(self) -> str:
        return self._coffee.description() + ", 加奶"

class SugarDecorator(CoffeeDecorator):
    def cost(self) -> float:
        return self._coffee.cost() + 0.8
    def description(self) -> str:
        return self._coffee.description() + ", 加糖"

class WhippedCreamDecorator(CoffeeDecorator):
    def cost(self) -> float:
        return self._coffee.cost() + 2.0
    def description(self) -> str:
        return self._coffee.description() + ", 奶油顶"

if __name__ == "__main__":
    print("=== 装饰器模式 ===")
    coffee = SimpleCoffee()
    print(f"{coffee.description()} 价格: {coffee.cost()}元")

    # 加奶
    coffee_with_milk = MilkDecorator(coffee)
    print(f"{coffee_with_milk.description()} 价格: {coffee_with_milk.cost()}元")

    # 加奶加糖
    coffee_with_milk_sugar = SugarDecorator(MilkDecorator(coffee))
    print(f"{coffee_with_milk_sugar.description()} 价格: {coffee_with_milk_sugar.cost()}元")

    # 豪华版:加奶+糖+奶油
    deluxe = WhippedCreamDecorator(SugarDecorator(MilkDecorator(coffee)))
    print(f"{deluxe.description()} 价格: {deluxe.cost()}元")

输出

bash 复制代码
=== 装饰器模式 ===
基础咖啡 价格: 5.0元
基础咖啡, 加奶 价格: 6.5元
基础咖啡, 加奶, 加糖 价格: 7.3元
基础咖啡, 加奶, 加糖, 奶油顶 价格: 9.3元

Python内置装饰器语法糖:函数装饰器也是该模式的体现。

bash 复制代码
import functools
def log_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__} 参数: {args} {kwargs}")
        result = func(*args, **kwargs)
        print(f"返回: {result}")
        return result
    return wrapper

@log_call
def add(a, b):
    return a + b

print(add(3, 5))

优缺点

  • 优点:比继承更灵活,避免类爆炸;符合开闭原则。

  • 缺点:多层装饰导致调试困难;可能产生很多小对象。

适用场景:动态添加功能(I/O流处理、GUI组件边框、日志记录等)。


3.3 代理模式(Proxy)

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

问题场景:需要延迟加载、访问控制、日志记录或远程访问。

解决方案:代理类与真实类实现同一接口,代理持有真实对象的引用并控制访问。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 抽象主题
class Image(ABC):
    @abstractmethod
    def display(self):
        pass

# 真实主题:大图片加载耗时
class HighResolutionImage(Image):
    def __init__(self, filename):
        self.filename = filename
        self.load_from_disk()
    def load_from_disk(self):
        print(f"正在从磁盘加载高清图片 {self.filename} (耗时3秒)...")
        # 模拟耗时
        import time
        time.sleep(0.5)  # 仅演示,实际可设为更长
        print(f"加载完成")
    def display(self):
        print(f"显示图片: {self.filename}")

# 代理:延迟加载
class ImageProxy(Image):
    def __init__(self, filename):
        self.filename = filename
        self.real_image = None
    def display(self):
        if self.real_image is None:
            self.real_image = HighResolutionImage(self.filename)
        self.real_image.display()

# 客户端
if __name__ == "__main__":
    print("=== 代理模式(延迟加载)===")
    img1 = ImageProxy("photo1.jpg")
    img2 = ImageProxy("photo2.jpg")

    print("第一次显示photo1:")
    img1.display()
    print("第二次显示photo1:")
    img1.display()
    print("显示photo2:")
    img2.display()

输出

bash 复制代码
=== 代理模式(延迟加载)===
第一次显示photo1:
正在从磁盘加载高清图片 photo1.jpg (耗时3秒)...
加载完成
显示图片: photo1.jpg
第二次显示photo1:
显示图片: photo1.jpg
显示photo2:
正在从磁盘加载高清图片 photo2.jpg (耗时3秒)...
加载完成
显示图片: photo2.jpg

其他常见代理

  • 保护代理(控制访问权限)

  • 远程代理(隐藏网络通信)

  • 虚拟代理(延迟加载)

保护代理示例

bash 复制代码
class BankAccount:
    def withdraw(self, amount):
        print(f"取款 {amount} 元")

class AccountProxy:
    def __init__(self, user_role):
        self.role = user_role
        self.account = BankAccount()
    def withdraw(self, amount):
        if self.role == "admin":
            self.account.withdraw(amount)
        else:
            print("权限不足,无法取款")

if __name__ == "__main__":
    proxy = AccountProxy("guest")
    proxy.withdraw(100)
    admin_proxy = AccountProxy("admin")
    admin_proxy.withdraw(500)

输出

优缺点

  • 优点:职责清晰,可控制访问,支持延迟加载。

  • 缺点:增加系统复杂度,可能降低响应速度。

适用场景:远程代理(RPC)、虚拟代理(大对象加载)、安全代理(权限校验)。


3.4 外观模式(Facade)

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

问题场景:复杂子系统依赖过多,客户端需要调用多个模块才能完成一个功能。

解决方案:创建外观类封装子系统的复杂交互。

代码示例

bash 复制代码
# 子系统类:音视频解码、投影仪、灯光、功放等
class DVDPlayer:
    def on(self):
        print("DVD播放器开机")
    def play(self, movie):
        print(f"播放电影 {movie}")
    def off(self):
        print("DVD播放器关机")

class Projector:
    def on(self):
        print("投影仪开机")
    def wide_screen_mode(self):
        print("设置为宽屏模式")
    def off(self):
        print("投影仪关机")

class SoundSystem:
    def on(self):
        print("音响系统开机")
    def set_volume(self, level):
        print(f"音量设置为 {level}")
    def off(self):
        print("音响系统关机")

class Lights:
    def dim(self, level):
        print(f"灯光调暗至 {level}%")
    def on(self):
        print("灯光全亮")

# 外观类:家庭影院
class HomeTheaterFacade:
    def __init__(self, dvd, projector, sound, lights):
        self.dvd = dvd
        self.projector = projector
        self.sound = sound
        self.lights = lights

    def watch_movie(self, movie):
        print("\n准备观看电影...")
        self.lights.dim(20)
        self.projector.on()
        self.projector.wide_screen_mode()
        self.sound.on()
        self.sound.set_volume(60)
        self.dvd.on()
        self.dvd.play(movie)
        print("开始放映!\n")

    def end_movie(self):
        print("\n关闭电影...")
        self.dvd.off()
        self.sound.off()
        self.projector.off()
        self.lights.on()
        print("电影结束,灯光亮起\n")

if __name__ == "__main__":
    dvd = DVDPlayer()
    projector = Projector()
    sound = SoundSystem()
    lights = Lights()
    home_theater = HomeTheaterFacade(dvd, projector, sound, lights)

    home_theater.watch_movie("阿凡达")
    home_theater.end_movie()

输出

bash 复制代码
准备观看电影...
灯光调暗至 20%
投影仪开机
设置为宽屏模式
音响系统开机
音量设置为 60
DVD播放器开机
播放电影 阿凡达
开始放映!


关闭电影...
DVD播放器关机
音响系统关机
投影仪关机
灯光全亮
电影结束,灯光亮起

优缺点

  • 优点:简化客户端调用,降低耦合度;分层结构更清晰。

  • 缺点:可能成为上帝对象,过度耦合所有子系统。

适用场景 :复杂库或框架的入口(如requests库简化HTTP调用);需要为复杂子系统提供简单接口。


4. 行为型模式

4.1 观察者模式(Observer)

定义:定义对象间一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都得到通知并自动更新。

问题场景:事件驱动系统、数据更新后需要刷新多个界面(如MVC中的模型与视图)。

解决方案:被观察者(Subject)维护观察者列表,状态变化时调用观察者更新方法。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 观察者接口
class Observer(ABC):
    @abstractmethod
    def update(self, temperature, humidity, pressure):
        pass

# 被观察者(主题)
class WeatherStation:
    def __init__(self):
        self.observers = []
        self.temperature = 0
        self.humidity = 0
        self.pressure = 0

    def attach(self, observer: Observer):
        if observer not in self.observers:
            self.observers.append(observer)

    def detach(self, observer: Observer):
        self.observers.remove(observer)

    def notify(self):
        for observer in self.observers:
            observer.update(self.temperature, self.humidity, self.pressure)

    def set_measurements(self, temp, hum, press):
        self.temperature = temp
        self.humidity = hum
        self.pressure = press
        self.notify()

# 具体观察者1:手机App显示
class PhoneDisplay(Observer):
    def update(self, temperature, humidity, pressure):
        print(f"手机App显示: 温度={temperature}°C, 湿度={humidity}%, 气压={pressure}hPa")

# 具体观察者2:大屏幕显示器
class LargeScreenDisplay(Observer):
    def update(self, temperature, humidity, pressure):
        print(f"大屏幕: 当前温度 {temperature}°C | 湿度 {humidity}%")

if __name__ == "__main__":
    weather_station = WeatherStation()
    phone = PhoneDisplay()
    large_screen = LargeScreenDisplay()

    weather_station.attach(phone)
    weather_station.attach(large_screen)

    print("=== 天气数据更新 ===")
    weather_station.set_measurements(25, 65, 1013)
    weather_station.set_measurements(27, 70, 1010)

    print("\n解绑手机App后:")
    weather_station.detach(phone)
    weather_station.set_measurements(26, 68, 1012)

输出

bash 复制代码
=== 天气数据更新 ===
手机App显示: 温度=25°C, 湿度=65%, 气压=1013hPa
大屏幕: 当前温度 25°C | 湿度 65%
手机App显示: 温度=27°C, 湿度=70%, 气压=1010hPa
大屏幕: 当前温度 27°C | 湿度 70%

解绑手机App后:
大屏幕: 当前温度 26°C | 湿度 68%

Python内置事件机制 :可以使用weakref避免内存泄漏或使用asyncio等。

优缺点

  • 优点:支持广播通信,松耦合(主题只知道观察者接口)。

  • 缺点:观察者太多或更新频繁影响性能;循环依赖可能导致死循环。

适用场景:事件处理系统、股票行情推送、GUI监听器、模型-视图-控制器(MVC)。


4.2 策略模式(Strategy)

定义:定义一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法独立于使用它的客户端而变化。

问题场景:多种支付方式、排序算法切换、折扣计算等。

解决方案:定义策略接口,具体策略实现算法,上下文类组合策略。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 策略接口
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

# 具体策略1:信用卡支付
class CreditCardPayment(PaymentStrategy):
    def __init__(self, card_number, cvv):
        self.card_number = card_number
        self.cvv = cvv
    def pay(self, amount):
        print(f"使用信用卡 {self.card_number[-4:]} 支付 {amount} 元,CVV验证通过")

# 具体策略2:支付宝支付
class AlipayPayment(PaymentStrategy):
    def __init__(self, account):
        self.account = account
    def pay(self, amount):
        print(f"支付宝账户 {self.account} 支付 {amount} 元")

# 具体策略3:微信支付
class WechatPayment(PaymentStrategy):
    def __init__(self, openid):
        self.openid = openid
    def pay(self, amount):
        print(f"微信用户 {self.openid} 支付 {amount} 元")

# 上下文:购物车
class ShoppingCart:
    def __init__(self):
        self.items = []
        self.payment_strategy = None

    def add_item(self, item, price):
        self.items.append((item, price))

    def total(self):
        return sum(price for _, price in self.items)

    def set_payment_strategy(self, strategy: PaymentStrategy):
        self.payment_strategy = strategy

    def checkout(self):
        total = self.total()
        if self.payment_strategy is None:
            raise Exception("请先设置支付策略")
        self.payment_strategy.pay(total)

if __name__ == "__main__":
    cart = ShoppingCart()
    cart.add_item("Python编程书", 79)
    cart.add_item("机械键盘", 299)
    print(f"购物车总金额: {cart.total()}元")

    # 使用信用卡支付
    cart.set_payment_strategy(CreditCardPayment("1234-5678-9012-3456", "123"))
    print("--- 信用卡支付 ---")
    cart.checkout()

    # 更换策略为支付宝
    cart.set_payment_strategy(AlipayPayment("alice@example.com"))
    print("--- 支付宝支付 ---")
    cart.checkout()

输出

bash 复制代码
购物车总金额: 378元
--- 信用卡支付 ---
使用信用卡 3456 支付 378 元,CVV验证通过
--- 支付宝支付 ---
支付宝账户 alice@example.com 支付 378 元

结合lambda简化策略(Python特色):

bash 复制代码
strategies = {
    "add": lambda x, y: x + y,
    "subtract": lambda x, y: x - y,
    "multiply": lambda x, y: x * y,
}
def execute_strategy(op, a, b):
    return strategies[op](a, b)

print(execute_strategy("add", 10, 5))   # 15
print(execute_strategy("multiply", 3, 4)) # 12

优缺点

  • 优点:消除条件语句,易于扩展新策略;策略可复用。

  • 缺点:客户端必须了解不同策略;增加对象数量。

适用场景:多种算法变体(排序、压缩、加密);避免长if-else或switch。


4.3 模板方法模式(Template Method)

定义:定义一个操作中的算法骨架,将某些步骤延迟到子类中实现。子类可以重定义算法的特定步骤而不改变算法结构。

问题场景:多个子类共享相同步骤,但某些步骤实现各异(如数据挖掘:读取数据、分析、输出报告)。

解决方案:抽象类定义模板方法(通常为final),其中调用基本方法(抽象或hook)。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 抽象类
class DataProcessor(ABC):
    # 模板方法
    def process(self):
        self.load_data()
        self.analyze_data()
        self.save_results()
        self.write_report()  # hook方法可选覆写

    def load_data(self):
        print("从文件加载数据...")

    @abstractmethod
    def analyze_data(self):
        pass

    def save_results(self):
        print("保存分析结果到数据库")

    # hook方法(默认实现,子类可覆盖)
    def write_report(self):
        print("生成标准报告")

# 具体子类:销售数据分析
class SalesDataProcessor(DataProcessor):
    def analyze_data(self):
        print("分析销售额趋势,计算同比增长率")

    def write_report(self):
        print("生成销售报告PDF,附带图表")

# 具体子类:用户行为分析
class UserBehaviorProcessor(DataProcessor):
    def analyze_data(self):
        print("分析用户点击流,计算转化率")

    # 使用默认的write_report,不覆盖

if __name__ == "__main__":
    print("=== 销售数据处理 ===")
    sales = SalesDataProcessor()
    sales.process()

    print("\n=== 用户行为数据处理 ===")
    behavior = UserBehaviorProcessor()
    behavior.process()

输出

bash 复制代码
=== 销售数据处理 ===
从文件加载数据...
分析销售额趋势,计算同比增长率
保存分析结果到数据库
生成销售报告PDF,附带图表

=== 用户行为数据处理 ===
从文件加载数据...
分析用户点击流,计算转化率
保存分析结果到数据库
生成标准报告

进阶:钩子方法控制流程

bash 复制代码
class CoffeeMaker(ABC):
    def prepare_recipe(self):
        self.boil_water()
        self.brew()
        self.pour_in_cup()
        if self.customer_wants_condiments():
            self.add_condiments()

    def boil_water(self):
        print("烧开水")
    def pour_in_cup(self):
        print("倒入杯中")
    @abstractmethod
    def brew(self):
        pass
    @abstractmethod
    def add_condiments(self):
        pass
    def customer_wants_condiments(self):
        return True  # 钩子

class Tea(CoffeeMaker):
    def brew(self):
        print("浸泡茶叶")
    def add_condiments(self):
        print("加柠檬")
    def customer_wants_condiments(self):
        # 询问用户
        answer = input("要加柠檬吗?(y/n): ")
        return answer.lower() == 'y'

Tea().prepare_recipe()

优缺点

  • 优点:复用代码,避免重复;符合好莱坞原则(不要调用我们,我们调用你)。

  • 缺点:每个不同实现需要子类,增加了复杂度;子类对模板方法的影响有限。

适用场景:框架基类、算法骨架固定且部分可变、工作流引擎。


4.4 状态模式(State)

定义:允许对象在其内部状态改变时改变它的行为,看起来好像修改了其类。

问题场景:对象的行为依赖于其状态,并且状态转换逻辑复杂(如电梯、订单状态机)。

解决方案:将状态封装为独立类,上下文委托给当前状态对象执行行为。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 状态接口
class State(ABC):
    @abstractmethod
    def handle(self, context):
        pass

# 具体状态:已订货
class OrderedState(State):
    def handle(self, context):
        print("订单已创建,等待支付")
        context.state = PaidState()  # 状态转移

# 具体状态:已支付
class PaidState(State):
    def handle(self, context):
        print("支付完成,正在备货")
        context.state = ShippedState()

# 具体状态:已发货
class ShippedState(State):
    def handle(self, context):
        print("已发货,等待确认收货")
        context.state = DeliveredState()

# 具体状态:已完成
class DeliveredState(State):
    def handle(self, context):
        print("订单已完成,感谢购买!")

# 上下文:订单
class Order:
    def __init__(self):
        self.state = OrderedState()

    def next_state(self):
        self.state.handle(self)

    def cancel(self):
        # 可定义取消逻辑,某些状态允许取消
        print("订单已取消")

if __name__ == "__main__":
    order = Order()
    order.next_state()  # 已订货 -> 等待支付
    order.next_state()  # 已支付 -> 备货
    order.next_state()  # 已发货 -> 等待收货
    order.next_state()  # 已完成
    order.next_state()  # 已完成状态无转移,将再次执行(但DeliveredState内部没有转移,只会打印)

输出

bash 复制代码
订单已创建,等待支付
支付完成,正在备货
已发货,等待确认收货
订单已完成,感谢购买!
订单已完成,感谢购买!

优化:在状态类中增加条件防止无限循环(实际开发中可以增加状态转换条件检查)。

电梯状态示例

bash 复制代码
class ElevatorState(ABC):
    @abstractmethod
    def open_door(self, elevator): pass
    @abstractmethod
    def close_door(self, elevator): pass
    @abstractmethod
    def move(self, elevator): pass

class IdleState(ElevatorState):
    def open_door(self, elevator):
        print("开门")
        elevator.state = DoorOpenState()
    def close_door(self, elevator):
        print("门已关,无法重复关闭")
    def move(self, elevator):
        print("空闲状态,未移动")

class DoorOpenState(ElevatorState):
    def open_door(self, elevator):
        print("门已开")
    def close_door(self, elevator):
        print("关门")
        elevator.state = IdleState()
    def move(self, elevator):
        print("门未关,不能移动")

class Elevator:
    def __init__(self):
        self.state = IdleState()
    def open(self): self.state.open_door(self)
    def close(self): self.state.close_door(self)
    def move(self): self.state.move(self)

e = Elevator()
e.open()
e.move()
e.close()
e.move()

优缺点

  • 优点:将状态转换逻辑局部化;增加新状态容易;减少条件分支。

  • 缺点:状态过多会增加类的数量;状态模式对开闭原则支持较好(增加状态不变现有代码)。

适用场景:有限状态机、游戏角色不同形态、订单/工作流状态管理。


4.5 责任链模式(Chain of Responsibility)

定义:使多个对象都有机会处理请求,避免请求发送者与接收者耦合。将这些对象连成一条链,沿着链传递请求直到被处理。

问题场景:审批流程(员工请假->主管->经理->HR)、日志级别处理、过滤器链。

解决方案:每个处理器持有下一个引用,处理不了就转发。

代码示例

bash 复制代码
from abc import ABC, abstractmethod

# 处理器抽象
class Handler(ABC):
    def __init__(self):
        self._next_handler = None

    def set_next(self, handler):
        self._next_handler = handler
        return handler

    @abstractmethod
    def handle(self, request):
        if self._next_handler:
            return self._next_handler.handle(request)
        return None

# 具体处理器1:技术负责人(可处理500元以下报销)
class TechLead(Handler):
    def handle(self, request):
        if request["type"] == "reimbursement" and request["amount"] <= 500:
            print(f"技术负责人审批了 {request['amount']} 元报销单")
            return True
        else:
            print("技术负责人无法处理,转交上级")
            return super().handle(request)

# 具体处理器2:项目经理(可处理1000元以下)
class ProjectManager(Handler):
    def handle(self, request):
        if request["type"] == "reimbursement" and request["amount"] <= 1000:
            print(f"项目经理审批了 {request['amount']} 元报销单")
            return True
        else:
            print("项目经理无法处理,转交上级")
            return super().handle(request)

# 具体处理器3:总监(可处理5000元以下)
class Director(Handler):
    def handle(self, request):
        if request["type"] == "reimbursement" and request["amount"] <= 5000:
            print(f"总监审批了 {request['amount']} 元报销单")
            return True
        else:
            print("总监无法处理,需要CEO批准")
            return super().handle(request)

if __name__ == "__main__":
    # 构建责任链
    tech_lead = TechLead()
    pm = ProjectManager()
    director = Director()
    tech_lead.set_next(pm).set_next(director)

    requests = [
        {"type": "reimbursement", "amount": 200},
        {"type": "reimbursement", "amount": 800},
        {"type": "reimbursement", "amount": 3000},
        {"type": "reimbursement", "amount": 10000},
    ]

    for req in requests:
        print(f"\n处理报销 {req['amount']} 元")
        result = tech_lead.handle(req)
        if not result:
            print(f"{req['amount']} 元报销未被任何领导审批")

输出

bash 复制代码
处理报销 200 元
技术负责人审批了 200 元报销单

处理报销 800 元
技术负责人无法处理,转交上级
项目经理审批了 800 元报销单

处理报销 3000 元
技术负责人无法处理,转交上级
项目经理无法处理,转交上级
总监审批了 3000 元报销单

处理报销 10000 元
技术负责人无法处理,转交上级
项目经理无法处理,转交上级
总监无法处理,需要CEO批准
10000 元报销未被任何领导审批

优缺点

  • 优点:降低耦合,增加灵活性;动态调整链。

  • 缺点:请求可能未被处理;调试困难(链路过长)。

适用场景:日志框架(不同级别去不同输出)、Servlet过滤器、事件冒泡。


5. 总结与下载说明

本文详细介绍了15种常见的设计模式,每种模式均提供了完整的Python代码示例实际运行输出,确保你可以复制代码并亲自运行验证。理解这些模式不仅能提升代码质量,还能让你在团队协作中使用统一的术语交流。

相关推荐
PSLoverS1 小时前
Python如何实现测试场景编排_基于pytest的数据驱动组合策略
jvm·数据库·python
不会写DN1 小时前
如何通过 Python 实现招聘平台自动投递
开发语言·前端·python
西贝爱学习1 小时前
Python3.13安装包及其下载地址
python
lbb 小魔仙1 小时前
Ollama + Python 本地大模型部署与API调用:从零开始搭建私有AI助手
开发语言·人工智能·python
邪修king1 小时前
C++ typename & auto 彻底讲透:核心作用、推导规则、避坑指南
开发语言·c++
会编程的土豆1 小时前
MySQL 多表查询
开发语言·数据库·python·mysql
2403_883261091 小时前
PHP调用Codex处理PHP特定语法【操作】
jvm·数据库·python
50万马克的面包2 小时前
三子棋小游戏(C语言详解)
c语言·开发语言·算法
旷世奇才李先生2 小时前
React 18\+Next\.js 14实战:服务端渲染与跨端开发全指南
java·人工智能·python