公众号:人生只不过是一场投资
引言
在软件开发中,设计模式是一套被反复使用、经过分类和总结的代码设计经验。被广泛用于解决常见的问题。在 Python 脚本设计中,创建对象的方式多种多样,设计模式提供了多种有效的解决方案。观察者模式(Observer Pattern)是一种行为型设计模式,旨在定义对象之间的一对多依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式特别适用于事件驱动的系统,通过松耦合的方式实现对象之间的通信。本文将探讨Python中的观察者模式,介绍其应用领域,提供代码实例及详解,并分析其优缺点,最终得出结论。
应用领域
观察者模式在以下几种场景中有广泛的应用:
- 事件处理系统:在图形用户界面(GUI)和实时系统中,观察者模式常用于事件处理机制,监听用户的操作并作出相应的反应。
- 数据变化通知:在数据模型与视图分离的架构中,当数据发生变化时,通过观察者模式通知视图更新显示。
- 消息广播系统:在发布-订阅(Publish-Subscribe)系统中,观察者模式用于实现消息的广播和接收。
- 日志系统:在需要记录系统行为的日志系统中,观察者模式可以实时通知日志记录器记录相关信息。
示例一
from abc import ABC, abstractmethod
from typing import List
# 抽象主题类
class Subject(ABC):
"""
主题接口,定义了添加、删除观察者以及通知观察者的方法
"""
def __init__(self):
self._observers: List[Observer] = [] # 存储观察者对象的列表
def attach(self, observer: 'Observer') -> None:
"""
添加观察者
"""
self._observers.append(observer)
def detach(self, observer: 'Observer') -> None:
"""
删除观察者
"""
self._observers.remove(observer)
def notify(self) -> None:
"""
通知所有观察者
"""
for observer in self._observers:
observer.update(self)
# 抽象观察者类
class Observer(ABC):
"""
观察者接口,定义了更新方法
"""
@abstractmethod
def update(self, subject: Subject) -> None:
"""
接收来自主题的通知,并进行相应的处理
"""
pass
# 具体主题类
class Data(Subject):
"""
具体主题类,存储数据并管理观察者
"""
def __init__(self, value: int):
super().__init__()
self._value = value
@property
def value(self) -> int:
return self._value
@value.setter
def value(self, new_value: int) -> None:
"""
当数据发生变化时,通知所有观察者
"""
self._value = new_value
self.notify()
# 具体观察者类
class HexObserver(Observer):
"""
十六进制观察者,将数据转换为十六进制并打印
"""
def update(self, subject: Data) -> None:
print(f"Hex Observer: {hex(subject.value)}")
# 具体观察者类
class BinaryObserver(Observer):
"""
二进制观察者,将数据转换为二进制并打印
"""
def update(self, subject: Data) -> None:
print(f"Binary Observer: {bin(subject.value)}")
# 实例化主题和观察者
data = Data(10)
hex_observer = HexObserver()
binary_observer = BinaryObserver()
# 将观察者添加到主题
data.attach(hex_observer)
data.attach(binary_observer)
# 修改数据,触发通知
data.value = 15
# 输出:
Hex Observer: 0xf
Binary Observer: 0b1111
代码解释:
- 抽象类:
Subject
和Observer
是抽象类,定义了主题和观察者的基本接口。 - 具体类:
Data
是具体的主题类,存储数据并管理观察者;HexObserver
和BinaryObserver
是具体的观察者类,分别将数据转换为十六进制和二进制并打印。 - 观察者模式: 当
Data
对象的数据发生变化时,会调用notify()
方法通知所有观察者。观察者接收到通知后,会调用update()
方法进行相应的处理。
这个案例展示了如何使用 Python 的观察者设计模式实现数据变化通知。当 Data
对象的数据发生变化时,HexObserver
和 BinaryObserver
会自动收到通知并进行相应的处理。
示例二
我们以一个股票市场系统为例,演示观察者模式的实现。该系统包含股票数据和多个投资者,当股票价格更新时,所有投资者都会收到通知并做出相应决策。
from abc import ABC, abstractmethod
# 定义一个主题接口:
class Subject(ABC):
@abstractmethod
def register_observer(self, observer):
pass
@abstractmethod
def remove_observer(self, observer):
pass
@abstractmethod
def notify_observers(self):
pass
# 定义一个观察者接口:
class Observer(ABC):
@abstractmethod
def update(self, temperature, humidity, pressure):
pass
# 定义投资者类,实现更新方法:
class Investor(Observer):
def __init__(self, name):
self.name = name
def update(self, stock, price):
print(f"{self.name} notified: {stock} price changed to {price}")
# 定义股票数据类,当价格变化时通知所有投资者:
class StockData(Subject):
def __init__(self):
self.observers = {}
self.prices = {}
def register_observer(self, stock, observer):
if stock not in self.observers:
self.observers[stock] = []
self.observers[stock].append(observer)
def remove_observer(self, stock, observer):
if stock in self.observers:
self.observers[stock].remove(observer)
def notify_observers(self, stock):
for observer in self.observers.get(stock, []):
observer.update(stock, self.prices[stock])
def set_price(self, stock, price):
self.prices[stock] = price
self.notify_observers(stock)
# 通过具体的股票数据和投资者实现通知机制:
stock_data = StockData()
investor1 = Investor("Alice")
investor2 = Investor("Bob")
stock_data.register_observer("AAPL", investor1)
stock_data.register_observer("GOOGL", investor2)
stock_data.set_price("AAPL", 150)
stock_data.set_price("GOOGL", 2800)
# 运行结果:
Alice notified: AAPL price changed to 150
Bob notified: GOOGL price changed to 2800
优点
- 解耦:观察者模式通过将观察者和被观察对象分离,降低了代码的耦合度,提高了系统的可维护性。
- 动态更新:当被观察对象的状态发生变化时,所有观察者都能及时得到通知并更新,实现了实时响应。
- 灵活性:观察者模式允许动态添加和移除观察者,增强了系统的灵活性和扩展性。
- 分布式通信:观察者模式适用于分布式系统中的事件通知和消息传递,简化了跨节点的通信。
缺点
- 通知延迟:如果观察者数量较多,通知的传播可能会导致一定的延迟,影响系统的实时性。
- 内存泄漏:如果未正确管理观察者的注册和注销,可能会导致内存泄漏问题,增加系统的资源消耗。
- 复杂性增加:在某些情况下,过多的观察者和通知机制可能会增加系统的复杂性,难以理解和维护。
结论
观察者模式在定义对象之间的一对多依赖关系方面具有显著的优势,尤其在事件处理系统、数据变化通知、消息广播系统和日志系统等场景中表现出色。尽管观察者模式存在通知延迟和内存泄漏等缺点,但其带来的解耦性和灵活性使其在实际开发中非常有价值。在实际应用中,应根据具体需求权衡利弊,合理使用观察者模式,以充分发挥其优势,避免其不足对系统造成影响。通过合适的设计和实现,观察者模式在Python应用中可以有效提高系统的实时性和可维护性。
利用观察者模式实现日志系统
from abc import ABC, abstractmethod
import logging
# 设置日志系统
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class Subject:
"""
主题类,负责维护观察者列表并通知观察者更新
"""
def __init__(self):
self._observers = []
self.state = None
def attach(self, observer):
"""添加观察者"""
logging.info(f"Attaching observer: {observer}")
self._observers.append(observer)
def detach(self, observer):
"""移除观察者"""
logging.info(f"Detaching observer: {observer}")
if observer in self._observers:
self._observers.remove(observer)
def notify(self):
"""通知所有观察者进行更新"""
logging.info("Notifying observers...")
for observer in self._observers:
observer.update(self)
def change_state(self, new_state):
"""改变主题状态并通知观察者"""
self.state = new_state
logging.info(f"State changed to: {new_state}")
self.notify()
class Observer(ABC):
"""
观察者抽象基类,定义观察者接口
"""
@abstractmethod
def update(self, subject):
"""观察者更新方法,由具体观察者实现"""
pass
class ConcreteObserverA(Observer):
"""具体观察者A"""
def update(self, subject):
logging.info(f"ConcreteObserverA: Reacted to the change. New state is: {subject.state}")
class ConcreteObserverB(Observer):
"""具体观察者B"""
def update(self, subject):
logging.info(f"ConcreteObserverB: Reacted to the change. New state is: {subject.state}")
if __name__ == "__main__":
# 创建主题
subject = Subject()
# 创建观察者
observer_a = ConcreteObserverA()
observer_b = ConcreteObserverB()
# 添加观察者
subject.attach(observer_a)
subject.attach(observer_b)
# 改变主题状态并通知观察者
subject.change_state("State 1")
# 移除观察者
subject.detach(observer_a)
# 再次改变主题状态并通知观察者
subject.change_state("State 2")
代码解释:
- 日志记录: 使用
logging
模块记录每个操作,例如添加/移除观察者、状态改变和通知。 - Subject 类:
- 维护观察者列表
_observers
。 attach
和detach
方法用于添加和移除观察者。notify
方法遍历观察者列表并调用每个观察者的update
方法。change_state
方法改变主题状态并调用notify
方法通知观察者。
- 维护观察者列表
- Observer 抽象基类:
- 定义
update
方法作为观察者接口。
- 定义
- ConcreteObserverA 和 ConcreteObserverB:
- 具体观察者类,实现
update
方法以响应主题状态改变。
- 具体观察者类,实现
运行结果:
控制台会输出以下日志信息:
2023-10-26 16:51:00,345 - INFO - Attaching observer: <__main__.ConcreteObserverA object at 0x7f93644d3460>
2023-10-26 16:51:00,345 - INFO - Attaching observer: <__main__.ConcreteObserverB object at 0x7f93644d3490>
2023-10-26 16:51:00,345 - INFO - State changed to: State 1
2023-10-26 16:51:00,346 - INFO - Notifying observers...
2023-10-26 16:51:00,346 - INFO - ConcreteObserverA: Reacted to the change. New state is: State 1
2023-10-26 16:51:00,346 - INFO - ConcreteObserverB: Reacted to the change. New state is: State 1
2023-10-26 16:51:00,346 - INFO - Detaching observer: <__main__.ConcreteObserverA object at 0x7f93644d3460>
2023-10-26 16:51:00,347 - INFO - State changed to: State 2
2023-10-26 16:51:00,347 - INFO - Notifying observers...
2023-10-26 16:51:00,347 - INFO - ConcreteObserverB: Reacted to the change. New state is: State 2
这表明观察者模式正常工作,观察者会响应主题状态的变化。