【Python设计模式】06 观察者模式-了解对象的情况
六、观察者模式-了解对象的情况
这章可以讨论行为型设计模式:观察者设计模式
本章主题
- 行为型设计模式简介
- 观察者设计模式及其 UML图
- 利用 Python3.x代码实现一个真实用例
- 松耦合的强大威力
- 常见问答
1. 行为型模式简介
-
创建型模式的工作原理是基于对象的创建机制的。由于这些模式隔离了对象的创建细节,所以使得代码能够与要创建的对象的类型相互独立。结构型模式用于设计对象和类的结构,从而使它可以相互协作以获得更大的结构。它们重点关注的是简化结构以及识别类和对象之间的关系
-
结构型模式用于设计对象和类的结构,从而使它们可以相互协作以获得更大的结构。重点在于简化结构以及识别类和对象之间的关系
-
行为型模式重点关注的是对象的责任。它们用来处理对象之间的交互,以实现更大的功能。行为型模式:对象之间应该能够彼此交互,同时还应该是松散耦合的
2. 理解观察者设计模式
在观察者设计模式中,对象(主题)维护了一个依赖(观察者)列表,以便主题可以使用观察者定义的任何方法通知所有观察者它所发生的变化
观察者模式的主要目标:
- 它定义了对象之间的一对多的依赖关系,从而使得一个对象中的任何更改都将自动通知给其他依赖对象
- 它封装了主题的核心组件
观察者模式的应用场景:
- 在分布式系统中实现事物服务
- 用作新闻机构的架构
- 股票市场也是观察者模式的一个大型场景
Python3.x实现简单的观察者模式
class Subject:
def __init__(self):
self.__observers = []
def register(self, observer):
self.__observers.append(observer)
def notifyAll(self, *args, **kwargs):
for observer in self.__observers:
observer.notify(self, *args, **kwargs)
class Observer1:
def __init__(self, subject):
subject.register(self)
def notify(self, subject, *args):
print(type(self).__name__, ":: Got", args, "From", subject)
class Observer2:
def __init__(self, subject):
subject.register(self)
def notify(self, subject, *args):
print(type(self).__name__, ":: Got", args, "From", subject)
subject = Subject()
observer1 = Observer1(subject)
observer2 = Observer2(subject)
subject.notifyAll("notification")
运行结果:
Observer1 :: Got (‘notification’,) From <main.Subject object at 0x7f9f99fc6518>
Observer2 :: Got (‘notification’,) From <main.Subject object at 0x7f9f99fc6518>
3. 观察者模式的 UML类图
观察者有三个主要角色:主题,观察者和具体观察者
-
主题:类Subject 需要了解Observer。Subject类具有许多方法,诸如 register()和 deregister()等,Observer可以通过这些方法注册到 Subject类中。因此,一个 Subject可以处理多个 Observer
-
观察者:它为关注主题的对象定义了一个接口。它定义了 Observer需要实现的各个方法,以便在主题发生变化时能够获得相应的通知
-
具体观察者(ConcreteObserver):它用来保存应该与 Subject的状态保持一致的状态。它实现了 Observer接口以保持其状态与主题中的变化相一致
4. 现实世界中的观察者模式
我们以新闻机构为例展示观察者模式的现实世界场景
# coding:utf-8
from abc import abstractmethod, ABCMeta
# 主题:
class NewPublisher:
def __init__(self):
self.__subscribers = []
self.__latestNews = None
# 提供注册
def attch(self, subscriber):
self.__subscribers.append(subscriber)
# 提供注销
def detach(self):
return self.__subscribers.pop()
# 返回已经订户列表
def subscribers(self):
return [type(x).__name__ for x in self.__subscribers]
# 返回注册的所有用户
def notifySubscribers(self):
for sub in self.__subscribers:
sub.update()
# 创建新消息
def addNews(self, news):
self.__latestNews = news
# 返回最新消息
def getNews(self):
return "Got News:", self.__latestNews
# 观察者:用户
class Subscriber(metaclass=ABCMeta):
@abstractmethod
def update(self):
pass
# 具体观察者:SMS用户
class SMSSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attch(self)
def update(self):
print(type(self).__name__, self.publisher.getNews())
# 具体观察者:邮件用户
class EmailSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attch(self)
def update(self):
print(type(self).__name__, self.publisher.getNews())
# 具体观察者:其他用户
class AnyOtherSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attch(self)
def update(self):
print(type(self).__name__, self.publisher.getNews())
if __name__ == "__main__":
news_publisher = NewPublisher()
for Subscribers in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:
Subscribers(news_publisher) # 观察者,向主题订阅
print("\nSubscriber: ", news_publisher.subscribers())
news_publisher.addNews("Hello World!") # 增加消息
news_publisher.notifySubscribers() # 发布消息
print("\nDetached: ", type(news_publisher.detach()).__name__) # 删除订阅者
print("\nSubscribers: ", news_publisher.subscribers()) # 查看订阅列表
news_publisher.addNews("My second news") # 增加消息
news_publisher.notifySubscribers() # 发布消息
运行结果:
Subscriber: [‘SMSSubscriber’, ‘EmailSubscriber’, ‘AnyOtherSubscriber’]
SMSSubscriber (‘Got News:’, ‘Hello World!’)
EmailSubscriber (‘Got News:’, ‘Hello World!’)
AnyOtherSubscriber (‘Got News:’, ‘Hello World!’)
Detached: AnyOtherSubscriber
Subscribers: [‘SMSSubscriber’, ‘EmailSubscriber’]
SMSSubscriber (‘Got News:’, ‘My second news’)
EmailSubscriber (‘Got News:’, ‘My second news’)
5. 观察者通知方式
- 拉模型
- 每当发生变化时,主题都会向所有已注册的观察者进行广播
- 出现变化时,观察者负责获取相应变化情况,或者从订户哪里拉取数据
- 拉模型效率较低,因为它涉及两个步骤。第一步,主题通知观察者;第二步,观察者从主题那里提取所需的数据
- 推模型
- 与拉模型不同,变化由主题推送到观察者
- 在拉模型中,主题可以向观察者发送详细的信息。当主题发送大量观察者用不到的数据时,会使响应时间过长
- 由于只从主题发送所需的数据,所以能够提高性能
6. 松耦合与观察者
松耦合是软件开发应该采用的重要设计原理之一
- 松耦合架构特性:
- 降低一个元素内发生的更改可能对其他元素产生意外影响的风险
- 它使得测试、维护和故障排除工作更加简单
- 系统可以轻松地分解为可定义的元素
- 观察者模式:
- 主题对观察者唯一了解的就是实现一个特定的接口。同时不需要了解具体的观察者类
- 可以随时添加任意的新观察者
- 添加新观察者,根本不需要修改主题
- 观察者和主题没有绑定在一起,可以彼此独立使用。如果有需要,观察者可以在任何地方重用
- 观察者中的变化不会相互影响
7. 观察者模式的优点和缺点
- 观察者模式优点:
- ta使得彼此交互的对象之间保持松耦合
- ta使得我们可以在无需对主题或观察者进行任何修改的情况下,高效地发送数据到其他对象
- 可以随时添加、删除观察者
- 观察者模式缺点:
- 观察者接口必须有具体观察者实现,而这涉及继承。无法进行组合,因为观察者接口可以实例化
- 如果实现不当的话,观察者可能会增加复杂性,并导致性能降低
- 在软件应用程序中,通知有时可能是不可靠的,并导致竞争条件或不一致
8. 常见问题
-
可能存在多个主题和观察者吗?
当一个软件应用程序建立多个主题和观察者时,是可能的。但是想要正常工作的话,需要通知观察者哪些主题发生了变化以及各个主题中发生了哪些变化 -
谁负责触发更新?
观察者模式可以在推模型和拉模型中工作。通常发生更新时,主题会触发更新方法,但有时可以根据应用程序需要,观察者也是可以触发通知。然而,需要注意是频率不应该太高,否则则可能导致性能下降,特别是当主题的更新不太频繁时 -
主题或观察者可以在任何其他用例中访问吗?
是的,这就是松散耦合的力量在观察者模式中的强大体现。主题和观察者是可以独立使用的
9. 小结
- 简要介绍行为型模式
- 观察者模式知识,以及如何将其高效地应用于软件架构中
- 考察了观察者模式如何用来通知观察者在主题中发生的变化。它们不仅能够管理对象之间的交互,同时还能管理对象的一对多的依赖关系
- 学习了观察者设计模式的 UML类图和基于 Python3.x的实例实现代码
- 观察者模式的两种不同实现方式:推模型和拉模型
- 软件设计的松耦合原理
- 常见问题的解答,了解观察者模式的思想以及优缺点