- UML类图简介
- 设计模式的分类
- 面向对象的设计原则
- python设计模式【1】-单例模式
- python设计模式【2】-工厂模式
- python设计模式【3】-门面模式
- python设计模式【4】-代理模式
- python设计模式【5】-观察者模式
- python设计模式【6】-命令模式
- python设计模式【7】-模板方法模式
- python设计模式【8】-模型·视图·控制器-复合模式
- python设计模式【9】-状态模式
观察者设计模式是最简单的行为型模式之一;行为型模式,它主要关注的是对象的责任。它们用来处理对象之间的交互,以实现更大的功能。行为型模式建议:对象之间应该能够彼此交互,同时还应该是松散耦合的。
理解观察者设计模式
在观察者设计模式中,对象(主题)维护了一个依赖(观察者)列表,以便主题可以使用观察者定义的任何方法通知所有观察者它所发生的变化。
在分布式应用的世界中,多个服务通常是通过彼此交互来实现用户想要实现的更大型的操作。服务可以执行多种操作,但是他们执行的操作会直接或很大程度上取决于与其交互的服务对象的状态。
观察者模式的主要目标如下:
1、它定义了对象之间一对多的依赖关系,从而使一个对象中的任何更改都将自动通知给其它依赖对象
2、他封装了主题的核心组件
观察者模式的应用场景:
1、在分布式系统中实现事件服务
2、用作新闻机构的框架
3、股票市场也是观察者模式的一个大型场景
Python代码的实现:
class Subject(object):
def __init__(self):
self.__observer = []
def register(self, observer):
self.__observer.append(observer)
def notify_all(self, *args, **kwargs):
for observer in self.__observer:
observer.notify(self, *args, **kwargs)
class Observer1(object):
def __init__(self, subject):
subject.register(self)
def notify(self, subject, *args):
print(type(self).__name__, 'Got', args, 'From', subject)
class Observer2(object):
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.notify_all('notification')
观察者模式三个主要角色:
1、主题(Subject):类Subject需要了解Observer。Subject类具有许多的方法,例如register()等,Observer可以通过这些方法注册到Subject类中。因此,一个Subject可以处理多个Observe。
2、观察者(Observer):它为关注主题的对象定义了一个接口。它定义了Observer需要实现的各个方法,以便主题发生变化时能够获得相应的通知;
3、具体观察者(ConcreteObserver):它用来保存应该与Subject的状态保持一致的状态。它实现了Observer接口以保持其状态与主题中的变化相一致。
这个流程非常简单。具体观察者通过实现观察者提供的接口向主题注册自己。每当状态发生变化时,该主题都会使用观察者提供的通知方法来通告所有具体观察者。
生活中的观察者模式
我们将以新闻机构为例来展示观察者模式的生活中的场景。新闻机构通常从不同地点收集新闻,并将其发布给订阅者。下面利用Python来开发一个应用程序,实现上面的用例。
从主题开始,这里的主题是新闻发布者:
1、主题的行为有NewsPublisher类表示
2、NewsPublisher提供了一个订阅用户使用的接口
3、attach()方法供观察者(Observer)来注册NewsPublisherObserver,detach()方法用于注销
4、subscriber()方法返回已经使用Subject注册所有订阅用户列表
5、notify_subscriber()方法可以用来遍历已向NewsPublisher注册的所有订阅用户
6、发布者可以使用add_news()方法创建新的消息,get_news()用于返回最新消息,并且通知观察者。
class NewsPublisher(object):
def __init__(self):
self.__subscribers = []
self.__latest_news = None
def 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 notify_subscribers(self):
for sub in self.__subscribers:
sub.update()
def add_news(self, news):
self.__latest_news = news
def get_news(self):
return "Got News:", self.__latest_news
现在我们讨论观察者(Observer)接口:
1、在这个例子中,Subscriber表示Observer,它是一个抽象的基类,代表其他ConcreteObserver;
2、Subscriber有一个update()方法,但是它需要由ConcreteObservers实现
3、update()方法是由ConcreteObserver实现的,这样只要有新闻发布时,它们都能够得到Subject的相应通知
from abc import ABCMeta, abstractmethod
class Subscriber(object):
@abstractmethod
def update(self):
pass
下面试具体观察者:
1、在本例中,我们有两个主要观察者,分别实现订阅用户接口的EmailSubscriber和SMSSubscriber
2、除了这两个外,我们建立了另外一个观察者AnyOtherObserver,它是用来演示Observers与Subject的松散耦合关系的
3、每个具体观察者的__init__()方法都是使用attach()方法向NewsPublisher进行注册
4、具体观察者的update()方法由NewsPublisher在内部用来通知添加新的新闻
class SMSSubscriber(object):
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.get_news())
class EmailSubscriber(object):
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.get_news())
class AnyOtherSubscriber(object):
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.get_news())
以下代码展示主题和观察者之间的交互:
if __name__ == "__main__":
news_publisher = NewsPublisher()
for Subscribers in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:
Subscribers(news_publisher)
print("\nSubscribers:", news_publisher.subscribers())
news_publisher.add_news("地球爆炸了")
news_publisher.notify_subscribers()
print("\nDetached:", type(news_publisher.detach()).__name__)
print("\nSubscribers:", news_publisher.subscribers())
news_publisher.add_news("二二二二二")
news_publisher.notify_subscribers()
观察者模式的通知方式
有两种不同的方式可以通知观察者在主题中发生的变化。它们可以被分为推模型或拉模型。
拉模型
在拉模型中,观察者扮演积极的角色。
1、每当发生变化时,主题都会向所有已经注册的观察者进行广播。
2、出现变化时,观察者负责获取相应的变化情况,或者从订户哪里拉取数据
3、拉模型的效率较低,因为它涉及两个步骤,第一步,主题通知观察者;第二步,观察者从主题那里提取所需的数据
推模型
在推模型中,主题是起到主导作用的一方,如下所示。
1、与拉模型不同,变化由主题推送到观察者的
2、在拉模型中,主题可以向观察者发送详情的信息(即使可能不需要)。当主题发送大量观察者用不到的数据时,会使响应时间过长。
3、由于主题发送所需的数据,所以能够提高性能
松耦合与观察者模式
松耦合使软件开发应该采用的重要设计原理之一。松耦合的主要目的使争取在彼此交互的对象之间实现松散耦合设计。耦合是指一个对象对于其交互的其他对象的了解程度。
松耦合设计允许我们构建灵活的面向对象的系统,有效应对各种变化,因为它们降低了多个对象之间的依赖。
松耦合架构具有以下特性:
1、它降低了在一个元素内发生的更改可能对其他元素产生意外影响的风险;
2、它使得测试/维护和故障排除工作更加简单;
3、系统可以轻松地分解为可定义的元素
观察者模式提供了一种实现主题和观察者松耦合的对象设计模式。以下几条可以更好地解释这一点。
1、主题对观察者唯一的了解就是它实现一个特定的接口。同时,它也不需要了解具体观察者类。
2、可以随时添加任意的新观察者
3、添加新的观察者时,根本不需要修改主题
4、观察者或主题没有绑定在一起,所以可以彼此独立使用
5、主题或观察者中的变化不会相互影响。由于两者都是独立的或松散耦合的,所以它们可以自由地做出自己的改变
观察者模式的优点和缺点
优点:
1、它使彼此交互的对象之间保持松耦合;
2、它使得我们可以在无需对主题或观察者进行任何修改得情况下高效地发送数据到其他对象
3、可以随时添加/删除观察者
缺点:
1、观察者接口必须由具体观察者实现,而这涉及继承。无法进行组合,因为观察者接口可以实例化
2、如果实现不当得话,观察者可能会增加复杂性,并导致性能降低
3、在软件应用程序中,通知有时可能不可靠的,并导致竞争条件或不一致性
参考:
《python设计模式》(第2版)https://www.epubit.com/