趣谈 Python 设计模式(1)观察者模式

当您觉得自己的代码写的没有建筑美感,对各种发行源码的书写方式表示费解的时候。就大概可以判断您的编程水平在懂得语法的玩具阶段。您可能逻辑很清晰,各种函数、类、对象、包也使用很熟练。但是当您反复看语言文档的时候会发现,总有一部分语法好像从来都没有使用过,如装饰器、迭代器等等。
这时候您所需要进阶的内容通常是这么几个关键词:元编程、设计模式、框架……
希望您能写出大师级的代码,一起加油ヾ(◍°∇°◍)ノ゙


观察者模式有两个角色组成观察者(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=ABCMetaabstractmethod 前者可以理解继承,后者是将 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("可以喝水啦!")

观察者模式的使用要点

  • 明确谁是观察者,谁是被观察者。比如 windows 上的一个窗口是被观察者,会有鼠标点击的观察者、键盘输入的观察者、位置变动的观察者,等等。
  • 被观察者至少要有三种方法:添加、移除、监听。观察者至少要有一个更新方法。
  • 此外它也还有其他的一些名字,发布/订阅(publish/subscribe)模式、模型/视图(Model/View)模式、源/监听器(Source/Listener)模式、监听模式、从属者模式。在观察者模型中,方法命名通常为 addObserver/removeObserver;在源/监视器模型中,叫做 attach/detach;在桌面窗口开发中叫做 attachWindow/detachWindow。他们都是观察者模型!

扩展知识:观察者模型有两种主要应用,推模型和拉模型。推模型中不管观察者是谁,都会给被观察者传递消息,如垃圾邮件推送服务。拉模型中,只会给观察者传递简单的信息,观察者看到消息时,根据需要主动向观察者获取更多的信息,如手机系统更新。
上面的例子是两种模式都支持的,拉模型和推模型跟多的是操作上的小区别。如果是推模型,observer 可以是空,推送的消息全部通过 obj 传递;拉模型时,observerobj 都传递消息,或只有 observer 传递消息,需要更具体的信息的时候再通过 observer 去取。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

西土城山羊卷

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

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

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

打赏作者

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

抵扣说明:

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

余额充值