python设计模式(三):观察者模式

引言

因为最近一直有在接触关于设计模式的一些案例,还有消息队列,使用了很多,而消息队列的背后其实就是一种设计模式,并且因为有写一个观察者模式的demo来初始化各种模型类,所以本篇想详细说说观察者模式的原理以及各种对比。

观察者模式介绍

观察者模式在软件设计中是一个对象,维护一个依赖列表,当任何状态发生改变自动通知它们。它能解决:

  1. 在不使对象紧密耦合的情况下,应定义对象之间的一对多依赖关系。
  2. 应确保当一个对象更改状态时,将自动更新不限数量的从属对象。
  3. 一个对象应该可以通知其他对象的不限数目的对象。


通过定义一个直接更新从属对象状态的对象(对象)来定义对象之间的一对多依赖关系是不灵活的,因为它将主题与特定的从属对象耦合在一起。从性能的角度来看,或者如果对象实现紧密耦合(从每秒执行数千次的低级内核结构来看),这仍然是有意义的。紧密耦合的对象在某些情况下可能难以实现,并且难以重用,因为它们引用并知道(以及如何更新)具有不同接口的许多不同对象。在其他情况下,紧密耦合的对象可能是更好的选择,因为编译器将能够在编译时检测错误并在CPU指令级别优化代码。

上述定义来自维基百科,如果仔细多读几遍,发现还是很好理解的。我举个例子,就像我在写这篇文章的时候,我需要想好整个题纲怎么去构思,分出几个小点,但当然有时候也会没有大纲想到哪就写啥。。。嗯,如果换成思维导图,那么就更好理解了,如果一个知识点没有一条完整的主线,等到下面的支线发生问题的时候,改动一个可能会导致连锁,然后越改越乱,会让整个结构很是臃肿。所以我们需要做的是那种可插拔式的,放入后,上一级分裂成二叉树自动进行关联,或者其它搜索树;取出,又恢复成单节点,就不需要我们手动配置。

而观察者模式中的通知也可以举个例子,我下面参考文献中有个例子很不错。假设某大公司找你去初试,面完后你觉得很不错,公司氛围融洽,薪资福利到位,对面hr也觉得OK,但最近你期待的岗位已经没有空缺了,但马上会空出来,所以等那个时候hr就会通知你以及其它候选人,然后再来一次复试,复试过了就如愿以偿。

所以这里的公司hr就是Subject,维护着Observers(和你一样的候选人),为某些event(比如职位空缺)来 通知(notify) 观察者。


这种通知-注册交互也称为 publish-subscribe

观察者与发布订阅

在观察者模式中的Subject就像一个发布者(Publisher),观察者(Observer)完全和订阅者(Subscriber)关联。subject通知观察者就像一个发布者通知他的订阅者。这也就是很多书和文章使用“发布-订阅”概念来解释观察者设计模式。 但是这里还有另外一个流行的模式叫做发布-订阅设计模式 。它的概念和观察者模式非常类似。最大如下图所是:

在这里插入图片描述
没错,就是中间再解耦的event channel。可以看出,发布订阅模式相比观察者模式多了个事件通道,事件通道作为调度中心,管理事件的订阅和发布工作,彻底隔绝了订阅者和发布者的依赖关系。即订阅者在订阅事件的时候,只关注事件本身,而不关心谁会发布这个事件;发布者在发布事件的时候,只关注事件本身,而不关心谁订阅了这个事件。

所以,可以总结以上两者的差异为:

  • 在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

  • 在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

  • 观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。

  • 观察者模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。

关于订阅发布的例子,之前有写过一篇python-kafka的搭建使用笔记,除了kafka,还有rabbitmq等一系列消息队列都是应用级的说明,下面就介绍观察者在python中的具体实现。


观察者实现

在python中,由于没有像Java一样支持观察者模式的标准库组件,所以python中观察者需要自己实现,最简单的方式是创建两个类为Observer和Observable类,具体实例如下。

观察者示例

class Publisher:
    def __init__(self):
        self.observers = []

    def add(self, observer):
        if observer not in self.observers:
            self.observers.append(observer)
        else:
            print("Failed to add: {}".format(observer))

    def remove(self, observer):
        try:
            self.observers.remove(observer)
        except ValueError:
            print("Failed to remove: {}".format(observer))

    def notify(self):
        [o.notify(self) for o in self.observers]


class DefaultFormatter(Publisher):
    def __init__(self, name):
        Publisher.__init__(self)
        self.name = name
        self._data = 0

    def __str__(self):
        return "{}: '{}' has data = {}".format(type(self).__name__, self.name, self._data)

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, new_value):
        try:
            self._data = new_value
        except ValueError as e:
            print("Error: {}".format(e))
        else:
            self.notify()


class HexFormatter:
    def notify(self, publisher):
        print("{}: '{}' has now hex data = {}".format(type(self).__name__, publisher.name, hex(publisher.data)))


class BinaryFormatter:
    def notify(self, publisher):
        print("{}: '{}' has now binary data = {}".format(type(self).__name__, publisher.name, bin(publisher.data)))


def main():
    df = DefaultFormatter('test1')
    print(df)

    print()
    hf = HexFormatter()
    df.add(hf)
    df.data = 3
    print(df)

    print()
    bf = BinaryFormatter()
    df.add(bf)
    df.data = 21
    print(df)

    print()
    df.remove(hf)
    df.data = 13
    print(df)


if __name__ == '__main__':
    main()

"""
DefaultFormatter: 'test1' has data = 0

HexFormatter: 'test1' has now hex data = 0x3
DefaultFormatter: 'test1' has data = 3

HexFormatter: 'test1' has now hex data = 0x15
BinaryFormatter: 'test1' has now binary data = 0b10101
DefaultFormatter: 'test1' has data = 21

BinaryFormatter: 'test1' has now binary data = 0b1101
DefaultFormatter: 'test1' has data = 13

"""

这是《python设计模式》书中第十三章观察者模式的比较经典的一个demo,这是打算使用观察者模式的模型或类都应该继承Publisher类。该类用set来保存观察者对象。当用户(HexFormatter和BinaryFormatter)向Publisher注册新的观察者对象时,观察者的update()方法会执行,这使得它能够用模型当前的状态初始化自己。模型状态发生变化时,应该调用继承而来的notify()方法,这样的话,就会执行每个观察者对象的update()方法,以确保他们都能反映出模型的最新状态。而DefaultFormatter类继承自Publisher,此类包含要被 广播 的数据,并在数据更新时调用通知观察者的方法。(这里的广播机制也类似于numpy数组,当然我这里只是提一嘴,可以忽略。)

看起来上面的例子还是有点冗余,可以参考文献2中的改进写法:

from abc import ABCMeta, abstractmethod


class NewsPublisher:  # subject
    def __init__(self):
        self.__subscribers = []
        self.__latestNews = None

    def attach(self, subscriber):
        self.__subscribers.append(subscriber)

    def detach(self):
        return self.__subscribers.pop()

    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):  # Observer

    @abstractmethod
    def update(self):
        pass


class ConcreteSubscriber1:  # ConcreteObserver
    def __init__(self, publisher):
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        print(type(self).__name__, self.publisher.getNews())


class ConcreteSubscriber2:
    def __init__(self, publisher):
        self.publisher = publisher
        self.publisher.attach(self)

    def update(self):
        print(type(self).__name__, self.publisher.getNews())


news_publisher = NewsPublisher()
for Subscribers in [ConcreteSubscriber1, ConcreteSubscriber2]:  # 创建观察者对象
    Subscribers(news_publisher)

news_publisher.addNews('HELLO WORLD')
news_publisher.notifySubscribers()
news_publisher.detach()
news_publisher.addNews('SECOND NEWS')
news_publisher.notifySubscribers()
'''
ConcreteSubscriber1 Got News:HELLO WORLD
ConcreteSubscriber2 Got News:HELLO WORLD
ConcreteSubscriber1 Got News:SECOND NEWS
'''

上述的ABCMeta,是约定俗称的实现这个方法,加上@abc.abstractmethod装饰器后严格控制子类必须实现这个方法,这也算是python里的一个多态性的地方,很久之前有写过一篇关于抽象类的博客,但当时还不太明白具体含义,只是在Django中看到有大规模使用,博客将在参考文献中引出。

观察者详解与进阶

照上面的代码看起来,算是完成了一个简单的demo,但似乎如果要弄成通用,或者说达到Java的级别,上述代码又过于简单,因为最近我也在学Java,关于Java为什么将观察者放于util中有所疑惑,但看到一篇外文有点懂了,比较大的原因可能是Java具有为线程同步提供内置支持的synced关键字。下面就从python角度写出这个类。

首先,我们可以尝试自己写一个提供thread synchronizationToSynch 类:

import threading
class ToSynch:
    def __init__(self):
        self.mutex = threading.RLock()
        self.val = 1
    def aSynchronizedMethod(self):
        self.mutex.acquire()
        try:
            self.val += 1
            return self.val
        finally:
            self.mutex.release()

但以这样的思路写下去是无聊并且难以再写,所以这个时候 Bruce Eckel 作者找到了另一个有相关研究的Peter Norvig ,直接用它提供的类和函数更加有效:

'''Simple emulation of Java's 'synchronized'
keyword, from Peter Norvig.'''
import threading

def synchronized(method):
    def f(*args):
        self = args[0]
        self.mutex.acquire()
        # print(method.__name__, 'acquired')
        try:
        	# return apply(method, args) 
            return method(*args)
        finally:
            self.mutex.release()
            # print(method.__name__, 'released')
    return f

def synchronize(klass, names=None):
    """Synchronize methods in the given class.
    Only synchronize the methods whose names are
    given, or all methods if names=None."""
    if type(names)==type(''): names = names.split()
    for (name, val) in klass.__dict__.items():
        if callable(val) and name != '__init__' and \
          (names == None or name in names):
            # print("synchronizing", name)
            setattr(klass, name, synchronized(val))

# You can create your own self.mutex, or inherit
# from this class:
class Synchronization:
    def __init__(self):
        self.mutex = threading.RLock()

这里我在原基础上修改了两个地方,一个是加了setattr,因为原文里好像没有任何修饰,但到我目前python3.7的版本会有报错,还有一个是return apply(method, args) ,我没有找到apply的方法在哪,后来查了下资料,更改成了return method。那么接着往下:

这个synchronized( ) 函数采用的方法和把它封装在其将所述互斥的功能,在某种程度上这是装饰器模式,但创建和使用起来要简单得多,你只需要用:

myMethod = synchronized(myMethod)

用互斥锁包围你的方法。

synchronize( ) 是一个遍历函数,而要让它用于工作,必须在每个使用synchronize( ) 的类中创建一个self.mutex,可以由类作者手动创建,但是使用继承更为一致,因此提供了基类 Synchronization。

这是对同步模块的简单测试:

# Util/TestSynchronization.py
from Synchronization import *

# To use for a method:
class C(Synchronization):
    def __init__(self):
        Synchronization.__init__(self)
        self.data = 1
    def m(self):
        self.data += 1
        return self.data
    m = synchronized(m)
    def f(self): return 47
    def g(self): return 'spam'

# So m is synchronized, f and g are not.
c = C()

# On the class level:
class D(C):
    def __init__(self):
        C.__init__(self)
    # You must override an un-synchronized method
    # in order to synchronize it (just like Java):
    def f(self): C.f(self)

# Synchronize every (defined) method in the class:
synchronize(D)
d = D()
d.f() # Synchronized
d.g() # Not synchronized
d.m() # Synchronized (in the base class)

class E(C):
    def __init__(self):
        C.__init__(self)
    def m(self): C.m(self)
    def g(self): C.g(self)
    def f(self): C.f(self)
# Only synchronizes m and g. Note that m ends up
# being doubly-wrapped in synchronization, which
# doesn't hurt anything but is inefficient:
synchronize(E, 'm g')
e = E()
e.f()
e.g()
e.m()

您必须为Synchronization调用基类构造函数,仅此而已。在C类中,您可以看到对m使用了synchronize( ),而使f和g保持不变。D类的所有方法都进行了同步,E类使用了便利函数来同步m和 g。请注意,由于m最终被同步了两次,因此每次调用都会被输入两次,并留下两次,这不是很理想[可能有解决方案]:

# Util/Observer.py
# Class support for "observer" pattern.
from Synchronization import *

class Observer:
    def update(observable, arg):
        '''Called when the observed object is
        modified. You call an Observable object's
        notifyObservers method to notify all the
        object's observers of the change.'''
        pass

class Observable(Synchronization):
    def __init__(self):
        self.obs = []
        self.changed = 0
        Synchronization.__init__(self)

    def addObserver(self, observer):
        if observer not in self.obs:
            self.obs.append(observer)

    def deleteObserver(self, observer):
        self.obs.remove(observer)

    def notifyObservers(self, arg = None):
        '''If 'changed' indicates that this object
        has changed, notify all its observers, then
        call clearChanged(). Each observer has its
        update() called with two arguments: this
        observable object and the generic 'arg'.'''

        self.mutex.acquire()
        try:
            if not self.changed: return
            # Make a local copy in case of synchronous
            # additions of observers:
            localArray = self.obs[:]
            self.clearChanged()
        finally:
            self.mutex.release()
        # Updating is not required to be synchronized:
        for observer in localArray:
            observer.update(self, arg)

    def deleteObservers(self): self.obs = []
    def setChanged(self): self.changed = 1
    def clearChanged(self): self.changed = 0
    def hasChanged(self): return self.changed
    def countObservers(self): return len(self.obs)

synchronize(Observable,
  "addObserver deleteObserver deleteObservers " +
  "setChanged clearChanged hasChanged " +
  "countObservers")

使用此库,这里是观察者模式的示例:

# Observer/ObservedFlower.py
# Demonstration of "observer" pattern.
import sys
sys.path += ['../util']
from Observer import Observer, Observable

class Flower:
    def __init__(self):
        self.isOpen = 0
        self.openNotifier = Flower.OpenNotifier(self)
        self.closeNotifier= Flower.CloseNotifier(self)
    def open(self): # Opens its petals
        self.isOpen = 1
        self.openNotifier.notifyObservers()
        self.closeNotifier.open()
    def close(self): # Closes its petals
        self.isOpen = 0
        self.closeNotifier.notifyObservers()
        self.openNotifier.close()
    def closing(self): return self.closeNotifier

    class OpenNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer
            self.alreadyOpen = 0
        def notifyObservers(self):
            if self.outer.isOpen and \
            not self.alreadyOpen:
                self.setChanged()
                Observable.notifyObservers(self)
                self.alreadyOpen = 1
        def close(self):
            self.alreadyOpen = 0

    class CloseNotifier(Observable):
        def __init__(self, outer):
            Observable.__init__(self)
            self.outer = outer
            self.alreadyClosed = 0
        def notifyObservers(self):
            if not self.outer.isOpen and \
            not self.alreadyClosed:
                self.setChanged()
                Observable.notifyObservers(self)
                self.alreadyClosed = 1
        def open(self):
            self.alreadyClosed = 0

class Bee:
    def __init__(self, name):
        self.name = name
        self.openObserver = Bee.OpenObserver(self)
        self.closeObserver = Bee.CloseObserver(self)
    # An inner class for observing openings:
    class OpenObserver(Observer):
        def __init__(self, outer):
            self.outer = outer
        def update(self, observable, arg):
            print("Bee " + self.outer.name + \
              "'s breakfast time!" )
    # Another inner class for closings:
    class CloseObserver(Observer):
        def __init__(self, outer):
            self.outer = outer
        def update(self, observable, arg):
            print("Bee " + self.outer.name + \
              "'s bed time!")

class Hummingbird:
    def __init__(self, name):
        self.name = name
        self.openObserver = \
          Hummingbird.OpenObserver(self)
        self.closeObserver = \
          Hummingbird.CloseObserver(self)
    class OpenObserver(Observer):
        def __init__(self, outer):
            self.outer = outer
        def update(self, observable, arg):
            print("Hummingbird " + self.outer.name + \
              "'s breakfast time!")
    class CloseObserver(Observer):
        def __init__(self, outer):
            self.outer = outer
        def update(self, observable, arg):
            print("Hummingbird " + self.outer.name + \
              "'s bed time!")

f = Flower()
ba = Bee("Eric")
bb = Bee("Eric 0.5")
ha = Hummingbird("A")
hb = Hummingbird("B")
f.openNotifier.addObserver(ha.openObserver)
f.openNotifier.addObserver(hb.openObserver)
f.openNotifier.addObserver(ba.openObserver)
f.openNotifier.addObserver(bb.openObserver)
f.closeNotifier.addObserver(ha.closeObserver)
f.closeNotifier.addObserver(hb.closeObserver)
f.closeNotifier.addObserver(ba.closeObserver)
f.closeNotifier.addObserver(bb.closeObserver)
# Hummingbird 2 decides to sleep in:
f.openNotifier.deleteObserver(hb.openObserver)
# A change that interests observers:
f.open()
f.open() # It's already open, no change.
# Bee 1 doesn't want to go to bed:
f.closeNotifier.deleteObserver(ba.closeObserver)
f.close()
f.close() # It's already closed; no change
f.openNotifier.deleteObservers()
f.open()
f.close()

在这里插入图片描述
感兴趣的事件是花朵可以打开或关闭。由于使用了内部类惯用语,因此这两个事件都是可以分别观察的现象。OpenNotifier和CloseNotifier都继承了Observable,因此它们可以访问setChanged(),并且可以传递给任何需要Observable的对象。

总结

本篇总结了关于观察者模式的原理,通过自造Synchronization实现了和Java类似的线程同步机制,但可能还是有些简单,因为观察者模式也算是各种应用级软件或者库都有使用,等之后更加精进了,会回头来修改相关知识点。



参考与推荐:
[1]. https://en.wikipedia.org/wiki/Observer_pattern

[2]. 观察者模式 vs 发布-订阅模式

[3]. python通用设计模式 第一版

[4]. python设计模式之观察者模式

[5]. Python 3 Patterns, Recipes and Idioms

[6]. Java设计模式之观察者模式的两种实现

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
观察者模式是一种行为设计模式,它用于在对象之间建立一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。 在Python中,可以使用以下几个元素实现观察者模式: 1. 主题(Subject):主题是被观察的对象,它维护了一个观察者列表,并提供了用于添加、删除和通知观察者的方法。 2. 观察者(Observer):观察者是依赖主题的对象,它定义了一个接收主题通知的方法。 3. 具体主题(Concrete Subject):具体主题是主题类的实现,它维护了一个状态,并在状态发生变化时发送通知给观察者。 4. 具体观察者(Concrete Observer):具体观察者是观察者类的实现,它实现了接收主题通知的方法,并根据通知进行相应的操作。 下面是一个简单的示例代码,演示了如何使用Python实现观察者模式: ```python class Subject: def __init__(self): self.observers = [] def attach(self, observer): self.observers.append(observer) def detach(self, observer): self.observers.remove(observer) def notify(self): for observer in self.observers: observer.update() class Observer: def update(self): pass class ConcreteSubject(Subject): def __init__(self, state): super().__init__() self.state = state def get_state(self): return self.state def set_state(self, state): self.state = state self.notify() class ConcreteObserver(Observer): def __init__(self, name): self.name = name def update(self): print(f"{self.name} received notification.") # 使用示例 subject = ConcreteSubject("Initial state") observer1 = ConcreteObserver("Observer 1") observer2 = ConcreteObserver("Observer 2") subject.attach(observer1) subject.attach(observer2) subject.set_state("New state") ``` 在上面的示例中,`Subject`类表示主题,`Observer`类表示观察者。`ConcreteSubject`和`ConcreteObserver`是它们的具体实现。当`ConcreteSubject`的状态发生变化时,它会通知所有的观察者,并调用观察者的`update`方法进行相应的处理。 希望这个示例能够帮助你理解Python中的观察者模式

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

submarineas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值