6.1行为型模式简介
观察者设计模式是最简单的行为型模式之一,所以我们先简单了解一下行为型模式
- 创建型模式的工作原理是基于对象的创建机制的。由于这些模式隔离了对象的创建细
节,所以使得代码能够与要创建的对象的类型相互独立。 - 结构型模式用于设计对象和类的结构,从而使它们可以相互协作以获得更大的结构。它们重点关注的是简化结构以及识别类和对象之间的关系。
- 行为型模式,顾名思义,它主要关注的是对象的责任。它们用来处理对象之间的交互,以实现更大的功能。
行为型模式建议:对象之间应该能够彼此交互,同时还应该是松散耦合的。
6.2 观察者设计模式
6.2.1 观察者设计模式介绍
在观察者设计模式中,对象(主题)维护了一个依赖(观察者)列表,以便主题可以使用观察者定义的任何方法通知所有观察者它所发生的变化。
- 场景1:从属服务监视核心服务的状态变化
例如用户在网站注册,其中用户服务组件负责用户在网站上的各种操作。假设我们有另外一个电子邮件的服务,它的作用是监视用户的状态并向用户发送电子邮件。在用户刚刚注册时,用户服务组件将调用电子邮件服务的方法,该方法将向用户发送电子邮件以进行账户验证。
如果账户经过了验证,但信用度较低,则电子邮件服务将监视用户服务并向用户发送信用度过低的电子邮件警报。
在这个网站应用中会存在一个许多其他服务所依赖的核心服务,该核心服务就是观察者观察/监视其变化的主题。
当主题发生变化时,观察者应该改变自己的对象的状态,或者采取某些动作。 - 场景2:广播或发布/订阅系统
例如在博客中你跟其他人一样共同关注了某个作者并进行了专栏订阅服务,每当作者发布新博客作品时,你和其他订阅者就会收到通知。
在观察者模式中,这里的博客就是维护订阅者或观察者列表的主题,当有新的文章添加到博客中时,所有观察者就会通过电子邮件或由观察者定义任何其他通知机制收到相应的通知。
6.2.2 观察者模式的主要作用:
- 它定义了对象之间的一对多的依赖关系,从而使得一个对象中的任何更改都将自动通知给其他依赖对象;
- 它封装了主题的核心组件观察者模式可用于以下多种场景;
- 在分布式系统中实现事件服务;
- 用作新闻机构的框架;
- 股票市场也是观察者模式的一个大型场景下面是观察者设计模式的Python实现;
6.2.3 观察者模式的UML类图
现在我们将通过UML图来深入了解观察者模式
正如我们在上面所讨论的那样,观察者模式有两个主要角色:主题和观察者。让我们把这些角色放在一个UML 图中,看看这些类是如何交互的,如图所示。
通过观察这个UML图你就会发现,这个模式有3个主要角色
- 主题(Subject):类Subject需要了解Observer。Subject类具有许多方法诸如register()和deregister()等,Observer可以通过这些方法注册到Subject 类中。
因此,一个Subject可以处理多个Observer。 - 观察者(observer):它为关注主题的对象定义了一个接口。它定义了observer需要实现的各个方法,以便在主题发生变化时能够获得相应的通知。
- 具体观察者(Concreteobserver): 它用来保存应该与 Subject 的状态保持一致的状态。它实现了observer接口以保持其状态与主题中的变化相一致。
这个流程非常简单。具体观察者通过实现观察者提供的接口向主题注册自己。每当状态发生变化时,该主题都会使用观察者提供的通知方法来通告所有具体观察者。
6.3 基于新闻发布案例的具体应用
6.3.1 需求场景:
新闻机构通常从不同地点收集新闻,并将其发布给订阅者。
由于信息是实时发送或接收的,所以新闻机构应该尽快向其订户公布该消息。
此外,随着技术的进步,订户不仅可以订阅报纸,而且可以通过其他的形式进行订阅,例如电子邮件、移动设备、短信或语音呼叫。
所以,我们还应该具备在将来添加任意其他订阅形式的能力,以便为未来的新技术做好准备。
6.3.2 代码实现
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
class NewsPublisher:# 新闻发布者,主题类:def __init__(self):self.subscribers = []self.latestNews = Nonedef attach(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 = newsdef getNews(self):# 返回新消息return "获取到消息:", self.latestNewsfrom abc import ABCMeta, abstractmethodclass Subscriber(metaclass=ABCMeta):"""观察者抽象基类,具体观察者继承该类,并实现抽象方法"""@abstractmethoddef update(self):# update()方法是由具体订阅者实现的,# 这样只要有新闻发布的时候它们都能得到 Subject(NewsPublishers)的相应通知。pass
class SMSSubscriber(Subscriber):"""短信观察者"""def __init__(self, publisher):# 初始化时进行注册self.publisher = publisherself.publisher.attach(self)def update(self):print(type(self).__name__, self.publisher.getNews())class EmailSubscriber(Subscriber):"""邮件观察者"""def __init__(self, publisher):self.publisher = publisherself.publisher.attach(self)def update(self):print(type(self).__name__, self.publisher.getNews())class AnyOtherSubscriber(Subscriber):# 其他观察者def __init__(self, publisher):self.publisher = publisherself.publisher.attach(self)def update(self):print(type(self).__name__, self.publisher.getNews())if __name__ == '__main__':news_publisher = NewsPublisher()for Subscribers in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:# 向主题中注册订阅者Subscribers(news_publisher)print("\n共有订阅者:", news_publisher.subscribers)news_publisher.addNews('添加消息:你好!')news_publisher.notifySubscribers()print("\n注销一个:", type(news_publisher.detach()).__name__)print("\n还剩订阅者:", news_publisher.subscribers)news_publisher.addNews('再次推送消息给订阅者')news_publisher.notifySubscribers()
6.4观察者模式的通知方式
有两种不同的方式可以通知观察者在主题中发生的变化。它们可以被分为推模型或拉模型。
6.4.1拉模型
在拉模型中,观察者扮演积极的角色
每当发生变化时,主题都会向所有已注册的观察者进行广播。
出现变化时,观察者负责获取相应的变化情况,或者从订户那里拉取数据。
拉模型的效率较低,因为它涉及两个步骤,第一步,主题通知观察者;第二步,观察者从主题那里提取所需的数据。
6.4.2 推模型
在推模型中,主题是起主导作用的一方,如下所示
与拉模型不同,变化由主题推送到观察者的。
在拉模型中,主题可以向观察者发送详细的信息(即使可能不需要)。当主题发送大量观察者用不到的数据时,会使响应时间过长。
由于只从主题发送所需的数据,所以能够提高性能。
6.5松耦合与观察者模式
松耦合是软件开发应该采用的重要设计原理之一。松耦合的主要目的是争取在彼此交互的对象之间实现松散耦合设计。
耦合是指一个对象对于与其交互的其他对象的了解程度。松耦合设计允许我们构建灵活的面向对象的系统,有效应对各种变化,因为它们降低了多个对象之间的依赖性。
松耦合架构具有以下特性:
- 它降低了在一个元素内发生的更改可能对其他元素产生意外影响的风险;
- 它使得测试、维护和故障排除工作更加简单;
- 系统可以轻松地分解为可定义的元素;
观察者模式提供了一种实现主题和观察者松耦合的对象设计模式。以下几条可以更好地解释这一点。 - 主题对观察者唯一的了解就是它实现一个特定的接口。同时,它也不需要了解具体观察者类。
- 可以随时添加任意的新观察者。
- 添加新的观察者时,根本不需要修改主题。在本示例中,我们看到任意其他观察者可以任意添加/删除,而无需在主题中进行任何的更改。
- 观察者或主题没有绑定在一起,所以可以彼此独立使用。如果需要的话,观察者可以在任何地方重复使用。
- 主题或观察者中的变化不会相互影响。由于两者都是独立的或松散耦合的,所以它们可以自由地做出自己的改变。
6.6 观察者模式:优点和缺点
6.6.1 观察者模式具有以下优点:
- 它使得彼此交互的对象之间保持松耦合;
- 它使得我们可以在无需对主题或观察者进行任何修改的情况下高效地发送数据到其他对象;
- 可以随时添加/删除观察者
6.6.2 观察者模式的缺点:
- 观察者接口必须由具体观察者实现,而这涉及继承。无法进行组合,因为观察者接口可以实例化;
- 如果实现不当的话,观察者可能会增加复杂性,并导致性能降低;
- 在软件应用程序中,通知有时可能是不可靠的,并导致竞争条件或不一致性。