当您觉得自己的代码写的没有建筑美感,对各种发行源码的书写方式表示费解的时候。就大概可以判断您的编程水平在懂得语法的玩具阶段。您可能逻辑很清晰,各种函数、类、对象、包也使用很熟练。但是当您反复看语言文档的时候会发现,总有一部分语法好像从来都没有使用过,如装饰器、迭代器等等。
这时候您所需要进阶的内容通常是这么几个关键词:元编程、设计模式、框架……
希望您能写出大师级的代码,一起加油ヾ(◍°∇°◍)ノ゙
观察者模式有两个角色组成观察者(observer)和被观察者(observable)。当您发现所面临的问题可以被抽象这两个角色的活动时,那么就可以按照此种设计模式来写代码。大师们已经对这类问题总结出了一套简洁的编程套路,我们只需要向这个套路中填空就好啦~
我们来假设一个场景。社畜程序员的你晚上10点早早的下了班,赶上13号线地铁回到你一个月4000块的20㎡的小别墅里,想洗个热水澡。但是你上世纪的热水器并不能智能控温,烧的太久就会烫掉一层皮。你想发了工资换个智能热水器,但是房东说改动房价要交1W块的房屋改装费。无奈,你只能写个程序来控制你的破热水器。
你想它有两个功能:1.水温达到50℃~70℃的时候,在屏幕上打印“可以洗澡啦!”;2. 水温达到100℃的时候,在屏幕上打印“可以喝热水啦!”
不难发现,这里面有两个角色,一个有加热功能的热水器,一个在一旁观察温度等待输出字符串的某某某。所以我们先定义一个热水器类:
class WaterHeater:
def __init__(self):
self.__temperature = 25
def setTemperature(self, temperature):
"""这里用手动设置温度来简化模拟自动加热的过程"""
self.__temperature = temperature
print("The current temperature is: " + str(self.__temperature))
def getTemperature(self):
return self.__temperature
被观察者很简单就被初步定义好了,接下来看看观察者应该怎么定义。
from abc import ABCMeta, abstractmethod
class Observer(metaclass=ABCMeta):
@abstractmethod
def update(self, waterHeater):
pass
先不用细究 metaclass=ABCMeta
和 abstractmethod
前者可以理解继承,后者是将 update
方法声明为抽象方法。它只有一个需要传入被观察者实例化对象的方法 update
。如果你是一个观察者的实例对象,你观察谁呢?肯定是观察被观察者的实例对象。你要观察他一点要能知道它的信息,所以一定要把他作为参数传进来。
因为在观察者模式中,被观察者只有一个,但是观察者可以有很多个。所以 Observer
类是所有观察的模子。
关于 @abstractmethod 的使用请自行查阅相关资料,关键词为:装饰器
假设我们有两个观察者,一个关注洗澡,一个关注饮用。只需要继承 Observer
重写就好了:
class WashingMode(Observer):
def update(self, waterHeater):
if waterHeater.getTemperature() >= 50 and waterHeater.getTemperature() < 70:
print("可以洗澡啦!")
class DrinkingMode(Observer):
def update(self, waterHeater):
if waterHeater.getTemperature() >= 100:
print("可以喝水啦!")
其实我们也完全可以使用一个观察者:
class WashingDrinkingMode(Observer):
def update(self, waterHeater):
if waterHeater.getTemperature() >= 50 and waterHeater.getTemperature() < 70:
print("可以洗澡啦!")
if waterHeater.getTemperature() >= 100:
print("可以喝水啦!")
现在我希望通过这几个类定义来运作这个功能啦,请看代码:
# 先实例化出观察者和被观察对象
heater = WaterHeater()
washingObser = WashingMode()
drinkingObser = DrinkingMode()
# 热水器开始加热,并保持观察
heater.setTemperature(30)
washingObser.update(heater)
drinkingObser.update(heater)
heater.setTemperature(60)
washingObser.update(heater)
drinkingObser.update(heater)
heater.setTemperature(80)
washingObser.update(heater)
drinkingObser.update(heater)
heater.setTemperature(100)
washingObser.update(heater)
drinkingObser.update(heater)
我们貌似实现了要求的功能,但是这种方法就跟你自己站在那里盯着热水器的温度表没有区别呀。我想要的是在我随意执行 heater.setTemperature()
的时候,突然某一次我的屏幕上就有输出了。我不应该手动去调用观察者的 update
方法。
不难想到,我应该让被观察者的属性变化(或者其他的什么变动后)自动的调用观察者的 update
方法。所以被观察者应该知道观察者的存在。所以观察者的实例对象,一定要成为被观察者的属性。于是修改一下代码:
class WaterHeater:
def __init__(self):
self.__observers= []
self.__temperature = 25
def setTemperature(self, temperature):
"""这里用手动设置温度来简化模拟自动加热的过程"""
self.__temperature = temperature
print("The current temperature is: " + str(self.__temperature))
def getTemperature(self):
return self.__temperature
def addObserver(self, observer):
self.__observers.append(observer)
这个给被观察者添加了一个观察者列表属性self.__observers
,并定义了 addObserver
方法,用于将观察者实例对象添加进列表中。此时采用下面的编写方法,被观察者就已经能够知晓都有哪些观察者了。
# 先实例化出观察者和被观察对象
heater = WaterHeater()
washingObser = WashingMode()
drinkingObser = DrinkingMode()
# 将观察者添加进被观察者的属性列表中
heater.addObserver(washingObser)
heater.addObserver(drinkingObser)
现在只需要在温度发生改变的时候,调用观察者的 update
方法就可以了。
class WaterHeater:
def __init__(self):
self.__observers= []
self.__temperature = 25
def setTemperature(self, temperature):
"""这里用手动设置温度来简化模拟自动加热的过程"""
self.__temperature = temperature
print("The current temperature is: " + str(self.__temperature))
self.notifies()
def getTemperature(self):
return self.__temperature
def addObserver(self, observer):
self.__observers.append(observer)
def notifies(self):
for o in self.__observers:
o.update(self)
增添了一个监听方法 notifies
,他会遍历整个观察者列表来执行其 update
方法,由于观察者的 update
方法需要通过被观察者的信息来判断自己应该做什么,因此将观察者对象 self
作为参数传递进来。
现在只需要在每次温度变化时 (执行 setTemperature
),调用 notifies
方法就可以了。
于是最终的运行程序为:
# 先实例化出观察者和被观察对象
heater = WaterHeater()
washingObser = WashingMode()
drinkingObser = DrinkingMode()
# 将观察者添加进被观察者的属性列表中
heater.addObserver(washingObser)
heater.addObserver(drinkingObser)
# 热水器开始加热,每次温度变化都会执行监听方法,通知各个观察者做出相应
heater.setTemperature(30)
heater.setTemperature(60)
heater.setTemperature(80)
heater.setTemperature(100)
现在来对观察者模式做一下官方性总结。
观察者模式就是在对象之间定义一种一对多的关系,可以有任意个观察者同时监听某一个对象。当被观察者在状态或者数据等你所关心的方面发生变化时,就会通知所有观察者对象,使他们做出相应的反应。
下面我们来使用一下标准观察者模式模板,来重新编写上面的代码
# 观察者模式的模型抽象
from abc import ABCMeta, abstractmethod
# 引入 ABCMeta 和 abstractmethod 来定义抽象类和抽象方法
class Observer(metaclass=ABCMeta):
"""观察者的基类"""
@abstractmethod
def update(self, observable, object)
pass
class Observable:
"""被观察者的基类"""
def __init__(self):
self.__observers = []
def addObserver(self, observer):
self.__observers.append(observer)
def removeObserver(self, observer):
self.__observers.remove(observer)
def notifyObservers(self, obj=0): # obj 代表其他参数
for o in self.__observers:
o.update(self, obj)
class WaterHeater(Observable):
def __init__(self):
super().__init__()
self.__temperature = 25
def setTemperature(self, temperature):
"""这里用手动设置温度来简化模拟自动加热的过程"""
self.__temperature = temperature
print("The current temperature is: " + str(self.__temperature))
self.notifyObservers()
def getTemperature(self):
return self.__temperature
class WashingMode(Observer):
def update(self, observable, obj):
if isinstance(observable, WaterHeater) \
and waterHeater.getTemperature() >= 50 and waterHeater.getTemperature() < 70:
print("可以洗澡啦!")
class DrinkingMode(Observer):
def update(self, waterHeater, obj):
if isinstance(observable, WaterHeater) and waterHeater.getTemperature() >= 100:
print("可以喝水啦!")
观察者模式的使用要点
addObserver/removeObserver
;在源/监视器模型中,叫做 attach/detach
;在桌面窗口开发中叫做 attachWindow/detachWindow
。他们都是观察者模型!扩展知识:观察者模型有两种主要应用,推模型和拉模型。推模型中不管观察者是谁,都会给被观察者传递消息,如垃圾邮件推送服务。拉模型中,只会给观察者传递简单的信息,观察者看到消息时,根据需要主动向观察者获取更多的信息,如手机系统更新。
上面的例子是两种模式都支持的,拉模型和推模型跟多的是操作上的小区别。如果是推模型,observer
可以是空,推送的消息全部通过obj
传递;拉模型时,observer
和obj
都传递消息,或只有observer
传递消息,需要更具体的信息的时候再通过observer
去取。