编程与数学 02-017 Python 面向对象编程 21课题、通用设计模式

编程与数学 02-017 Python 面向对象编程 21课题、通用设计模式

摘要:本文介绍了 Python 面向对象编程中的 10 种常用设计模式,包括单例模式、工厂模式、策略模式、观察者模式、装饰器模式、代理模式、模板方法模式、适配器模式、命令模式和组合模式。每种模式都通过示例代码展示了其实现方式、优点、缺点及适用场景。这些设计模式能够有效提高代码的可维护性、可扩展性和可读性,帮助开发者更好地解决实际问题。
关键词:设计模式,Python,面向对象,单例模式,工厂模式,策略模式,观察者模式,装饰器模式,代理模式,模板方法模式,适配器模式,命令模式,组合模式
人工智能助手:Kimi


一、单例模式(Singleton Pattern)

单例模式确保一个类只有一个实例,并提供一个全局访问点。

实现方式
  • 使用模块(Python 中模块是天然的单例)。
  • 使用类变量和 __new__() 方法。
示例代码
python 复制代码
class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

# 测试
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2)  # 输出:True
优点
  • 确保全局只有一个实例。
  • 提供全局访问点。
缺点
  • 可能与单测框架冲突。
  • 不符合开闭原则,难以扩展。
适用场景
  • 配置管理器。
  • 日志记录器。
  • 数据库连接池。

二、工厂模式(Factory Pattern)

工厂模式用于创建对象,而无需指定具体的类。它通过一个工厂类来封装对象的创建逻辑。

实现方式
  • 简单工厂模式。
  • 工厂方法模式。
  • 抽象工厂模式。
示例代码(简单工厂模式)
python 复制代码
class Dog:
    def speak(self):
        return "Woof!"

class Cat:
    def speak(self):
        return "Meow!"

class AnimalFactory:
    def get_animal(self, animal_type):
        if animal_type == "dog":
            return Dog()
        elif animal_type == "cat":
            return Cat()
        else:
            raise ValueError("Unknown animal type")

# 测试
factory = AnimalFactory()
dog = factory.get_animal("dog")
cat = factory.get_animal("cat")
print(dog.speak())  # 输出:Woof!
print(cat.speak())  # 输出:Meow!
优点
  • 将对象的创建逻辑封装起来,便于扩展。
  • 减少客户端代码的依赖。
缺点
  • 工厂类可能过于复杂。
适用场景
  • 创建多种类型的对象。
  • 隐藏对象的创建细节。

三、策略模式(Strategy Pattern)

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

实现方式
  • 定义一个策略接口。
  • 实现多种策略类。
  • 在上下文中动态切换策略。
示例代码
python 复制代码
from abc import ABC, abstractmethod

class Strategy(ABC):
    @abstractmethod
    def execute(self, a, b):
        pass

class AddStrategy(Strategy):
    def execute(self, a, b):
        return a + b

class SubtractStrategy(Strategy):
    def execute(self, a, b):
        return a - b

class Context:
    def __init__(self, strategy):
        self.strategy = strategy

    def set_strategy(self, strategy):
        self.strategy = strategy

    def execute_strategy(self, a, b):
        return self.strategy.execute(a, b)

# 测试
context = Context(AddStrategy())
print(context.execute_strategy(10, 5))  # 输出:15

context.set_strategy(SubtractStrategy())
print(context.execute_strategy(10, 5))  # 输出:5
优点
  • 算法可以独立于客户端变化。
  • 符合开闭原则。
缺点
  • 策略类数量可能过多。
适用场景
  • 算法需要动态切换。
  • 多种算法需要复用。

四、观察者模式(Observer Pattern)

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,所有依赖于它的观察者对象都会得到通知并自动更新。

实现方式
  • 定义主题接口和观察者接口。
  • 主题维护观察者列表。
  • 主题状态改变时通知所有观察者。
示例代码
python 复制代码
from abc import ABC, abstractmethod

class Observer(ABC):
    @abstractmethod
    def update(self, message):
        pass

class Subject:
    def __init__(self):
        self._observers = []

    def register_observer(self, observer):
        self._observers.append(observer)

    def notify_observers(self, message):
        for observer in self._observers:
            observer.update(message)

class ConcreteObserver(Observer):
    def __init__(self, name):
        self.name = name

    def update(self, message):
        print(f"{self.name} received message: {message}")

# 测试
subject = Subject()
observer1 = ConcreteObserver("Observer1")
observer2 = ConcreteObserver("Observer2")

subject.register_observer(observer1)
subject.register_observer(observer2)

subject.notify_observers("Hello, observers!")  # 输出:
# Observer1 received message: Hello, observers!
# Observer2 received message: Hello, observers!
优点
  • 低耦合。
  • 支持广播通信。
缺点
  • 通知顺序不确定。
  • 观察者可能过多。
适用场景
  • 事件驱动系统。
  • GUI 编程。

五、装饰器模式(Decorator Pattern)

装饰器模式动态地给一个对象添加额外的功能,而不改变其结构。装饰器模式是一种替代继承的方案。

实现方式
  • 定义一个组件接口。
  • 创建具体组件类。
  • 创建装饰器类,实现组件接口,并在内部包含一个组件对象。
示例代码
python 复制代码
from abc import ABC, abstractmethod

class Component(ABC):
    @abstractmethod
    def operation(self):
        pass

class ConcreteComponent(Component):
    def operation(self):
        return "ConcreteComponent"

class Decorator(Component):
    def __init__(self, component):
        self._component = component

    @abstractmethod
    def operation(self):
        pass

class ConcreteDecoratorA(Decorator):
    def operation(self):
        return f"ConcreteDecoratorA({self._component.operation()})"

class ConcreteDecoratorB(Decorator):
    def operation(self):
        return f"ConcreteDecoratorB({self._component.operation()})"

# 测试
component = ConcreteComponent()
decorator_a = ConcreteDecoratorA(component)
decorator_b = ConcreteDecoratorB(decorator_a)

print(decorator_b.operation())  # 输出:ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))
优点
  • 动态扩展功能。
  • 符合开闭原则。
缺点
  • 装饰器过多可能导致代码复杂。
适用场景
  • 动态添加功能。
  • 替代继承。

六、代理模式(Proxy Pattern)

代理模式为其他对象提供一种代理以控制对这个对象的访问。代理可以为被代理对象添加额外的逻辑。

实现方式
  • 定义一个代理类,实现与被代理类相同的接口。
  • 在代理类中封装被代理对象。
  • 在代理类中添加额外的逻辑。
示例代码
python 复制代码
from abc import ABC, abstractmethod

class Subject(ABC):
    @abstractmethod
    def request(self):
        pass

class RealSubject(Subject):
    def request(self):
        return "RealSubject: Handling request."

class Proxy(Subject):
    def __init__(self):
        self._real_subject = None

    def request(self):
        if self._real_subject is None:
            self._real_subject = RealSubject()
        return f"Proxy: Logging the time of request.\n{self._real_subject.request()}"

# 测试
proxy = Proxy()
print(proxy.request())  # 输出:
# Proxy: Logging the time of request.
# RealSubject: Handling request.
优点
  • 控制对对象的访问。
  • 添加额外的逻辑。
缺点
  • 代理类可能过于复杂。
适用场景
  • 控制对对象的访问。
  • 添加额外的逻辑。

七、模板方法模式(Template Method Pattern)

模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下重写算法的某些步骤。

实现方式
  • 定义一个模板方法,包含算法的骨架。
  • 在模板方法中调用抽象方法或钩子方法。
  • 子类实现抽象方法或钩子方法。
示例代码
python 复制代码
from abc import ABC, abstractmethod

class AbstractClass(ABC):
    def template_method(self):
        self.base_operation1()
        self.required_operations1()
        self.base_operation2()
        self.hook1()
        self.required_operations2()
        self.base_operation3()
        self.hook2()

    def base_operation1(self):
        print("AbstractClass says: I am doing the bulk of the work")

    def base_operation2(self):
        print("AbstractClass says: But I let subclasses override some operations")

    def base_operation3(self):
        print("AbstractClass says: But I am doing the bulk of the work anyway")

    @abstractmethod
    def required_operations1(self):
        pass

    @abstractmethod
    def required_operations2(self):
        pass

    def hook1(self):
        pass

    def hook2(self):
        pass

class ConcreteClass1(AbstractClass):
    def required_operations1(self):
        print("ConcreteClass1 says: Implemented Operation1")

    def required_operations2(self):
        print("ConcreteClass1 says: Implemented Operation2")

class ConcreteClass2(AbstractClass):
    def required_operations1(self):
        print("ConcreteClass2 says: Implemented Operation1")

    def required_operations2(self):
        print("ConcreteClass2 says: Implemented Operation2")

    def hook1(self):
        print("ConcreteClass2 says: Overridden Hook1")

# 测试
print("Client: I am using ConcreteClass1:")
client_code(ConcreteClass1())
print("\n")
print("Client: I am using ConcreteClass2:")
client_code(ConcreteClass2())
优点
  • 提供算法的骨架。
  • 子类可以灵活扩展。
缺点
  • 子类可能过多。
适用场景
  • 算法骨架固定。
  • 子类需要扩展。

八、适配器模式(Adapter Pattern)

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

实现方式
  • 类适配器:通过继承实现。
  • 对象适配器:通过组合实现。
示例代码(对象适配器)
python 复制代码
class MediaPlayer:
    def play(self, audio_type, filename):
        pass

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

    def play_mp4(self, filename):
        pass

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

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

class AudioPlayer:
    def play(self, audio_type, filename):
        if audio_type == "mp3":
            print(f"Playing mp3 file. Name: {filename}")
        elif audio_type in ("vlc", "mp4"):
            media_adapter = MediaAdapter(audio_type)
            media_adapter.play(audio_type, filename)
        else:
            print(f"Invalid media. {audio_type} format not supported")

# 测试
audio_player = AudioPlayer()
audio_player.play("mp3", "beyond_the_horizon.mp3")
audio_player.play("mp4", "alone.mp4")
audio_player.play("vlc", "far_far_away.vlc")
优点
  • 提供兼容接口。
  • 符合开闭原则。
缺点
  • 适配器可能过多。
适用场景
  • 接口不兼容。
  • 需要复用已有代码。

九、命令模式(Command Pattern)

命令模式将请求封装为一个对象,从而使用户可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

实现方式
  • 定义命令接口。
  • 创建具体命令类。
  • 创建调用者和接收者。
示例代码
python 复制代码
from abc import ABC, abstractmethod

class Command(ABC):
    @abstractmethod
    def execute(self):
        pass

class Light:
    def turn_on(self):
        print("Light is on")

    def turn_off(self):
        print("Light is off")

class LightOnCommand(Command):
    def __init__(self, light):
        self._light = light

    def execute(self):
        self._light.turn_on()

class LightOffCommand(Command):
    def __init__(self, light):
        self._light = light

    def execute(self):
        self._light.turn_off()

class RemoteControl:
    def __init__(self):
        self._commands = []

    def add_command(self, command):
        self._commands.append(command)

    def execute_commands(self):
        for command in self._commands:
            command.execute()

# 测试
light = Light()
light_on = LightOnCommand(light)
light_off = LightOffCommand(light)

remote = RemoteControl()
remote.add_command(light_on)
remote.add_command(light_off)

remote.execute_commands()  # 输出:
# Light is on
# Light is off
优点
  • 请求封装为对象。
  • 支持撤销操作。
缺点
  • 命令类可能过多。
适用场景
  • 请求需要排队。
  • 支持撤销操作。

十、组合模式(Composite Pattern)

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

实现方式
  • 定义组件接口。
  • 创建叶子组件和复合组件。
  • 在复合组件中管理子组件。
示例代码
python 复制代码
from abc import ABC, abstractmethod

class Component(ABC):
    @abstractmethod
    def operation(self):
        pass

class Leaf(Component):
    def operation(self):
        return "Leaf"

class Composite(Component):
    def __init__(self):
        self._children = []

    def add(self, component):
        self._children.append(component)

    def remove(self, component):
        self._children.remove(component)

    def operation(self):
        results = []
        for child in self._children:
            results.append(child.operation())
        return f"Branch({', '.join(results)})"

# 测试
leaf1 = Leaf()
leaf2 = Leaf()
composite = Composite()
composite.add(leaf1)
composite.add(leaf2)

print(composite.operation())  # 输出:Branch(Leaf, Leaf)
优点
  • 提供一致的接口。
  • 支持递归结构。
缺点
  • 可能过于复杂。
适用场景
  • 部分-整体结构。
  • 树形结构。

全文总结

本文详细介绍了 Python 面向对象编程中常用的 10 种设计模式,每种模式都通过具体的示例代码展示了其实现方式、优点、缺点及适用场景。单例模式确保全局只有一个实例,适用于配置管理器和日志记录器等场景;工厂模式通过工厂类封装对象创建逻辑,便于扩展;策略模式允许动态切换算法,符合开闭原则;观察者模式支持低耦合的广播通信,适用于事件驱动系统;装饰器模式动态扩展功能,避免了继承的复杂性;代理模式为对象访问添加额外逻辑;模板方法模式定义算法骨架,子类可灵活扩展;适配器模式解决接口不兼容问题;命令模式将请求封装为对象,支持撤销操作;组合模式通过树形结构表示部分 - 整体关系,提供一致的接口。这些设计模式在实际开发中具有重要的指导意义,能够有效提升代码质量,帮助开发者更好地应对复杂多变的编程需求。