Python中的观察者设计模式(指南)

155 阅读6分钟

简介

_软件设计模式_通过为你的代码提供一个可重复使用的蓝图来解决一个特定的问题,从而帮助加速开发过程。我们遵循设计模式来编写通用的、可重用的、可读的代码,这些代码可以被熟悉我们所应用的模式的其他人轻松理解。

它们封装了软件工程师解决相同问题的累积经验,并代表了常见的设计相关问题的解决方案。

设计模式有不同的分类,这取决于它们解决哪一类问题--其中的 观察者设计模式属于 行为模式类。

这类模式决定了对象之间的通信方式。在本指南中,你将学习关于观察者设计模式的所有知识,了解我们如何使用它来有效地解决某些问题。

观察者设计模式

的。 观察者设计模式处理的是 一对多关系,并利用_事件_来让_订阅的实体_了解_可观察到_的变化。

这些事件的来源被称为 主题可观察的它以流的形式发送事件。观察者 观察者可以订阅观察者以获得事件。观察者保持对观察者列表的跟踪,并在观察者的状态发生变化时通知他们相关变化。

Architecture Diagram of Observer Design Pattern - shows a news feed as an observable and multiple readers as observers subscribed to the news feed

这个功能有很多含义和实现,类似的功能就在你身边。这是一个极其简单,但非常有效和广泛的模式。

这种设计模式的类似实现见于在你的社交平台上生成feeds--即 Pub/Sub(发布者/订阅者)模式/模式.当内容发布者发布他们的帖子时,订阅者会得到内容的通知。一个类似的比喻可能是人们在寻找某个事件的信号弹或烟花,并根据他们的具体角色做出反应(或不反应)。

这是否意味着 观察者设计模式发布/订阅模式是一样的吗?

以前,这两种模式都是同义词。如今,每种模式都有明显的特征,使它们成为两种独立的模式。

以下是观察者模式和Pub/Sub模式的主要区别。

  • 观察者和主体是紧密耦合的。主体必须跟踪他们的观察者。而在Pub/Sub模式中,它们是松散耦合的,在观察者和主体之间有一个消息队列。
  • 事件以同步的方式从主体传递给观察者。但在Pub/Sub模式中,事件是异步传递的。
  • 在观察者模式中,主体和观察者都驻扎在同一个应用位置上,而在Pub/Sub模式中,他们可以驻扎在不同的位置上。

感受这种模式的最好方法之一是实现它,让我们在Python中实现它吧!

实现

一个基本的实现需要两个类--一个Observable 和一个ObserverObserver 类用一个对象作为参数进行初始化。这个对象不是别的,而是一个要跟踪的Observable ,它在创建时被订阅。

该类还有一个notify() 函数,用于触发反应,并确认从可观察到的通知/事件的接收。

class Observer:

    def __init__(self, observable):
        observable.subscribe(self)

    def notify(
        self,
        observable,
        *args,
        **kwargs
        ):
        print ('Got', args, kwargs, 'From', observable)

Observable 类被初始化为一个空列表,以容纳Observer 实例。它还有一些函数,如:subscribe() 来添加观察者,notify_observers() 来调用每个观察者的notify() 函数,以及unsubscribe() 来从列表中删除观察者。

class Observable:

    def __init__(self):
        self._observers = []

    def subscribe(self, observer):
        self._observers.append(observer)

    def notify_observers(self, *args, **kwargs):
        for obs in self._observers:
            obs.notify(self, *args, **kwargs)

    def unsubscribe(self, observer):
        self._observers.remove(observer)

插上上述所有的组件,让我们写一些代码,设置一个观察者和可观察者,并发送消息,从而触发一个反应。

# observer_pattern.py

"""
Demonstrating the Observer pattern implementation
"""
# Initializing the subject
subject = Observable()

# Initializing twp observers with the subject object
observer1 = Observer(subject)
observer2 = Observer(subject)

# The following message will be notified to 2 observers
subject.notify_observers('This is the 1st broadcast',
                         kw='From the Observer')
subject.unsubscribe(observer2)

# The following message will be notified to just 1 observer since
# the observer has been unsubscribed
subject.notify_observers('This is the 2nd broadcast',
                         kw='From the Observer')

请注意,在发布第二条消息之前,我们还取消了一个观察者的订阅。这将导致消息在第二次尝试时只被打印一次而不是两次,因为它只被一个订阅者收到。

运行这段代码将导致。

$ python observer_pattern.py
Got ('This is the 1st broadcast',) {'kw': 'From the Observer'} From <__main__.Observable object at 0x7f6c50d2fb50>
Got ('This is the 1st broadcast',) {'kw': 'From the Observer'} From <__main__.Observable object at 0x7f6c50d2fb50>
Got ('This is the 2nd broadcast',) {'kw': 'From the Observer'} From <__main__.Observable object at 0x7f6c50d2fb50>

正如你所看到的,可观察者可以直接与观察者交互,反之亦然。只要观察者订阅了观察者的订阅列表,观察者就会与观察者进行交互。

优点和缺点

随着实现的到位,这种设计模式的优点和缺点可以被比较如下。

优点。

  • 对象之间定义了一对多的关系。这确保了当一个对象被改变时,它将导致一连串的改变,并应用于依赖对象。

  • 松散耦合的对象意味着组件可以互换。

缺点。

  • 观察者和观察者之间的通信是同步的,随着订阅和取消订阅事件的负载增加,观察者对象可能会受到请求的轰炸。这可以通过为每个请求设置一个睡眠时间来缓解。

  • 睡眠解决方案也可能会导致速度、性能和事件的损失。这是Pub/Sub模式的主要原因,在发布者和订阅者之间有一个消息队列。

  • 在这种模式中,内存泄漏是很常见的,因为观察者和可观察者之间有很强的参照。观察者需要强制性地从可观察对象中取消注册。

为了减轻大部分弊端,在观察者和可观察者之间引入了一个消息队列,以克服所有这些问题,这导致了Pub/Sub模式的设计--观察者模式的一个变种。

总结

本指南介绍了观察者模式,如何实现它,并比较了它的优点和缺点。

有趣的是,观察者模式是行为模式之一,它导致了我们今天使用的许多功能,如RSS订阅、社交媒体订阅,等等。

通过对设计模式的细微差别的介绍,我们更容易从头开始构建功能。当然,了解不同的设计模式可以让你为不同类型的问题建立最好的解决方案。