各位观众,各位朋友,大家好!我是你们的老朋友,江湖人称“代码诗人”的李白(当然不是那个写诗的李白,我是写Python的李白!)。今天,咱们就来聊聊Python世界里那些既实用又有趣的“设计模式”。
什么?设计模式?听起来是不是很高大上?别怕,其实它们就像武林秘籍,教你如何用更优雅、更高效的方式解决代码中的难题。今天,咱们就挑三个最常用的——工厂模式、单例模式和观察者模式,用最通俗易懂的方式,把它们玩转于股掌之间!
准备好了吗?咱们这就开始!🚀
第一章:工厂模式 – 生产线上的魔法师
想象一下,你是一家玩具工厂的老板。你每天的工作就是生产各种各样的玩具:汽车、飞机、机器人… 如果你每接到一个订单,就手把手地去制造,那可就累死了!这时候,你就需要一个“玩具工厂”,它能根据你的指令,自动生产出你想要的玩具。
这就是工厂模式的精髓:创建一个对象时,不需要指定具体的类,而是通过一个工厂来创建。 就像你只需要告诉工厂“我要一辆汽车”,它就会帮你搞定,而你不需要关心汽车是怎么组装的。
1.1 简单工厂模式:一个顶俩
简单工厂模式是最简单的一种,它就像一个万能的工匠,什么都能造。
class Car:
def __init__(self):
self.name = "Car"
def drive(self):
return "Driving a car!"
class Plane:
def __init__(self):
self.name = "Plane"
def fly(self):
return "Flying a plane!"
class ToyFactory:
def create_toy(self, toy_type):
if toy_type == "car":
return Car()
elif toy_type == "plane":
return Plane()
else:
return None
# 使用
factory = ToyFactory()
car = factory.create_toy("car")
print(car.drive()) # 输出: Driving a car!
plane = factory.create_toy("plane")
print(plane.fly()) # 输出: Flying a plane!
这个例子中,ToyFactory
就是我们的简单工厂,它根据 toy_type
来创建不同的玩具对象。
优点:
- 简单易懂,容易实现。
缺点:
- 违反了开闭原则:如果要增加新的玩具类型,就需要修改
ToyFactory
的代码。这可不好,咱们应该尽量避免修改已有的代码。 - 所有玩具都由一个工厂创建,职责过于集中。
1.2 工厂方法模式:分工合作更高效
为了解决简单工厂模式的缺点,我们引入了工厂方法模式。它将创建对象的任务分配给不同的子工厂。
from abc import ABC, abstractmethod
class Toy(ABC): # 抽象玩具类
@abstractmethod
def play(self):
pass
class Car(Toy):
def play(self):
return "Playing with a car!"
class Plane(Toy):
def play(self):
return "Playing with a plane!"
class ToyFactory(ABC): # 抽象工厂类
@abstractmethod
def create_toy(self):
pass
class CarFactory(ToyFactory):
def create_toy(self):
return Car()
class PlaneFactory(ToyFactory):
def create_toy(self):
return Plane()
# 使用
car_factory = CarFactory()
car = car_factory.create_toy()
print(car.play()) # 输出: Playing with a car!
plane_factory = PlaneFactory()
plane = plane_factory.create_toy()
print(plane.play()) # 输出: Playing with a plane!
在这个例子中,我们定义了一个抽象的 ToyFactory
类,以及它的两个子类 CarFactory
和 PlaneFactory
。每个子工厂负责创建特定的玩具对象。
优点:
- 符合开闭原则:要增加新的玩具类型,只需要增加一个新的工厂类即可,不需要修改已有的代码。
- 职责分明:每个工厂只负责创建一种类型的玩具。
缺点:
- 增加了类的数量,代码复杂度有所提高。
1.3 抽象工厂模式:生产线上的顶级配置
如果我们的玩具工厂不仅要生产汽车和飞机,还要生产它们的零部件,比如发动机、轮胎、机翼等等,而且不同的汽车和飞机可能需要不同的零部件,这时候,我们就需要抽象工厂模式。
from abc import ABC, abstractmethod
# 抽象产品
class Engine(ABC):
@abstractmethod
def create(self):
pass
class Tire(ABC):
@abstractmethod
def create(self):
pass
# 具体产品
class CarEngine(Engine):
def create(self):
return "Car Engine"
class CarTire(Tire):
def create(self):
return "Car Tire"
class PlaneEngine(Engine):
def create(self):
return "Plane Engine"
class PlaneTire(Tire):
def create(self):
return "Plane Tire"
# 抽象工厂
class AbstractFactory(ABC):
@abstractmethod
def create_engine(self):
pass
@abstractmethod
def create_tire(self):
pass
# 具体工厂
class CarFactory(AbstractFactory):
def create_engine(self):
return CarEngine()
def create_tire(self):
return CarTire()
class PlaneFactory(AbstractFactory):
def create_engine(self):
return PlaneEngine()
def create_tire(self):
return PlaneTire()
# 使用
car_factory = CarFactory()
car_engine = car_factory.create_engine()
car_tire = car_factory.create_tire()
print(car_engine.create()) # 输出: Car Engine
print(car_tire.create()) # 输出: Car Tire
plane_factory = PlaneFactory()
plane_engine = plane_factory.create_engine()
plane_tire = plane_factory.create_tire()
print(plane_engine.create()) # 输出: Plane Engine
print(plane_tire.create()) # 输出: Plane Tire
在这个例子中,AbstractFactory
定义了创建 Engine
和 Tire
的接口,CarFactory
和 PlaneFactory
分别负责创建汽车和飞机的零部件。
优点:
- 可以创建一系列相关的产品对象,而无需指定它们具体的类。
- 客户端代码与具体的产品类解耦。
缺点:
- 增加了系统的抽象性和复杂性。
总结:
设计模式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
简单工厂模式 | 简单易懂,容易实现 | 违反开闭原则,职责过于集中 | 对象创建逻辑简单,且创建的对象类型较少的情况。 |
工厂方法模式 | 符合开闭原则,职责分明 | 增加了类的数量,代码复杂度有所提高 | 需要扩展对象类型,但又不想修改已有代码的情况。 |
抽象工厂模式 | 可以创建一系列相关的产品对象,而无需指定它们具体的类。客户端代码与具体的产品类解耦。 | 增加了系统的抽象性和复杂性 | 需要创建一系列相关的产品对象,且这些对象之间存在依赖关系的情况。例如,创建不同操作系统的 UI 元素(按钮、文本框等)。 |
工厂模式就像一个生产线上的魔法师,它能帮你轻松创建各种各样的对象,让你的代码更加灵活、可维护。
第二章:单例模式 – 独一无二的存在
在现实世界中,有些东西是独一无二的,比如地球只有一个,太阳也只有一个。在编程世界中,有些类也只需要一个实例,比如数据库连接池、配置管理器等等。
单例模式就是用来保证一个类只有一个实例,并提供一个全局访问点。
2.1 饿汉式单例:迫不及待的吃货
饿汉式单例在类加载的时候就创建了实例,就像一个迫不及待的吃货,一上来就狼吞虎咽。
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not isinstance(cls._instance, cls):
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
print("Singleton initialized")
# 使用
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # 输出: True
在这个例子中,我们重写了 __new__
方法,确保只创建一个实例。
优点:
- 实现简单,线程安全。
缺点:
- 在类加载的时候就创建了实例,可能会造成资源浪费。
2.2 懒汉式单例:姗姗来迟的美人
懒汉式单例在第一次使用的时候才创建实例,就像一个姗姗来迟的美人,犹抱琵琶半遮面。
import threading
class Singleton:
_instance = None
_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
print("Singleton initialized")
# 使用
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # 输出: True
在这个例子中,我们使用了一个锁 _lock
来保证线程安全。
优点:
- 延迟加载,只有在使用的时候才创建实例,可以节省资源。
缺点:
- 实现相对复杂,需要考虑线程安全问题。
2.3 使用元类实现单例:优雅的魔法
Python 的元类可以让我们在类创建的时候做一些手脚,从而实现单例模式。
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
def __init__(self):
print("Singleton initialized")
# 使用
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # 输出: True
在这个例子中,我们定义了一个元类 SingletonMeta
,它重写了 __call__
方法,确保只创建一个实例。
优点:
- 代码简洁优雅。
缺点:
- 理解元类需要一定的Python基础。
总结:
实现方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
饿汉式单例 | 实现简单,线程安全 | 在类加载的时候就创建了实例,可能会造成资源浪费 | 实例创建开销小,且总是会被使用的情况。 |
懒汉式单例 | 延迟加载,只有在使用的时候才创建实例,可以节省资源 | 实现相对复杂,需要考虑线程安全问题 | 实例创建开销大,且不一定会被使用的情况。 |
元类实现 | 代码简洁优雅 | 理解元类需要一定的Python基础 | 对代码简洁性有较高要求,且对Python元类有一定了解的情况。 |
单例模式就像一个独一无二的存在,它能保证一个类只有一个实例,让你的代码更加规范、高效。
第三章:观察者模式 – 消息灵通的百晓生
想象一下,你订阅了一个新闻频道,一旦有新的新闻发布,你就会立即收到通知。这就是观察者模式的精髓:定义对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
观察者模式也被称为发布-订阅模式,它就像一个消息灵通的百晓生,能让你及时掌握最新的动态。
3.1 基本实现:简单的消息通知
class Subject: # 被观察者
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
def notify(self, message):
for observer in self._observers:
observer.update(message)
class Observer: # 观察者
def update(self, message):
print(f"Received message: {message}")
# 使用
subject = Subject()
observer1 = Observer()
observer2 = Observer()
subject.attach(observer1)
subject.attach(observer2)
subject.notify("Hello, observers!") # 输出: Received message: Hello, observers! (两次)
subject.detach(observer1)
subject.notify("Hello again!") # 输出: Received message: Hello again! (一次)
在这个例子中,Subject
是被观察者,它维护了一个观察者列表 _observers
,并提供了 attach
、detach
和 notify
方法。Observer
是观察者,它定义了 update
方法,用于接收消息。
优点:
- 被观察者和观察者之间解耦。
- 可以动态地添加和删除观察者。
缺点:
- 如果观察者太多,可能会影响性能。
- 如果观察者和被观察者之间存在循环依赖,可能会导致无限循环。
3.2 使用抽象类:更灵活的扩展
为了更好地扩展,我们可以使用抽象类来定义观察者和被观察者的接口。
from abc import ABC, abstractmethod
class Subject(ABC): # 抽象被观察者
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def detach(self, observer):
self._observers.remove(observer)
@abstractmethod
def notify(self, message):
pass
class Observer(ABC): # 抽象观察者
@abstractmethod
def update(self, message):
pass
class ConcreteSubject(Subject): # 具体被观察者
def notify(self, message):
for observer in self._observers:
observer.update(message)
class ConcreteObserver(Observer): # 具体观察者
def update(self, message):
print(f"Received message: {message}")
# 使用
subject = ConcreteSubject()
observer1 = ConcreteObserver()
observer2 = ConcreteObserver()
subject.attach(observer1)
subject.attach(observer2)
subject.notify("Hello, observers!") # 输出: Received message: Hello, observers! (两次)
subject.detach(observer1)
subject.notify("Hello again!") # 输出: Received message: Hello again! (一次)
在这个例子中,我们定义了抽象的 Subject
和 Observer
类,以及具体的 ConcreteSubject
和 ConcreteObserver
类。
优点:
- 更灵活地扩展。
- 可以定义多个观察者和被观察者的实现。
缺点:
- 增加了代码的复杂性。
3.3 使用 Python 内置的 collections.abc
模块:更 Pythonic 的实现
Python 的 collections.abc
模块提供了一些抽象基类,可以让我们更方便地实现观察者模式。
import collections.abc
class Subject:
def __init__(self):
self._observers = set()
def attach(self, observer):
if not isinstance(observer, collections.abc.Callable):
raise TypeError("Observer must be callable")
self._observers.add(observer)
def detach(self, observer):
self._observers.discard(observer)
def notify(self, message):
for observer in self._observers:
observer(message)
# 使用
subject = Subject()
def observer1(message):
print(f"Observer 1 received: {message}")
def observer2(message):
print(f"Observer 2 received: {message}")
subject.attach(observer1)
subject.attach(observer2)
subject.notify("Hello, observers!") # 输出: Observer 1 received: Hello, observers! Observer 2 received: Hello, observers!
subject.detach(observer1)
subject.notify("Hello again!") # 输出: Observer 2 received: Hello again!
在这个例子中,我们使用了 collections.abc.Callable
来确保观察者是一个可调用对象。
优点:
- 更 Pythonic 的实现。
- 使用了 Python 内置的模块,代码更加简洁。
缺点:
- 需要了解 Python 内置的模块。
总结:
实现方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
基本实现 | 被观察者和观察者之间解耦。可以动态地添加和删除观察者。 | 如果观察者太多,可能会影响性能。如果观察者和被观察者之间存在循环依赖,可能会导致无限循环。 | 对象之间存在一对多的依赖关系,且需要动态地添加和删除观察者的情况。例如,GUI 中的事件处理。 |
使用抽象类 | 更灵活地扩展。可以定义多个观察者和被观察者的实现。 | 增加了代码的复杂性。 | 需要定义多个观察者和被观察者的实现,且需要更灵活的扩展的情况。 |
使用collections.abc |
更 Pythonic 的实现。使用了 Python 内置的模块,代码更加简洁。 | 需要了解 Python 内置的模块。 | 追求代码简洁性,且对 Python 内置模块有一定了解的情况。 |
观察者模式就像一个消息灵通的百晓生,它能让你及时掌握最新的动态,让你的代码更加灵活、可扩展。
总结:设计模式,代码的艺术
各位朋友,今天我们一起学习了Python中的三种常用的设计模式:工厂模式、单例模式和观察者模式。它们就像武林秘籍,教你如何用更优雅、更高效的方式解决代码中的难题。
设计模式并不是一成不变的,它们只是解决问题的思路和方法,我们需要根据实际情况灵活运用。记住,代码的艺术在于简洁、优雅和可维护。
希望今天的分享对大家有所帮助!如果大家还有什么问题,欢迎在评论区留言,我会尽力解答。
谢谢大家!🙏