設(shè)計(jì)模式之監(jiān)聽模式
??本文將會(huì)介紹設(shè)計(jì)模式中的監(jiān)聽模式。
??監(jiān)聽模式是一種一對多的關(guān)系,可以有任意個(gè)(一個(gè)或多個(gè))觀察者對象同時(shí)監(jiān)聽某一個(gè)對象。監(jiān)聽的對象叫觀察者(Observer),被監(jiān)聽的對象叫作被觀察者(Observable)。被觀察者對象在狀態(tài)或內(nèi)容(數(shù)據(jù))發(fā)生變化時(shí),會(huì)通知所有觀察者對象,使它們能夠做出相應(yīng)的變化。
??監(jiān)聽模式又名觀察者模式,顧名思義就是觀察與被觀察的關(guān)系。比如你在燒開水的時(shí)候看著它開沒開,你就是觀察者,水就是被觀察者;再比如你在帶小孩,你關(guān)注他是不是餓了,是不是渴了,是不是撒尿了,你就是觀察者,小孩就是被觀察者。觀察者模式是對象的行為模式,又叫發(fā)布/訂閱(Publish/Subscribe)模式、模型/視圖(Model/View)模式、源/監(jiān)聽器(Source/Listener)模式或從屬者(Dependents)模式。
??監(jiān)聽模式的核心思想就是在被觀察者與觀察者之間建立一種自動(dòng)觸發(fā)的關(guān)系。
例子
??以家中常見的熱水器為例,需對水溫進(jìn)行監(jiān)控,當(dāng)水溫在50-70℃時(shí),會(huì)發(fā)出警告:可以用來洗澡了!當(dāng)水溫在100℃時(shí),會(huì)發(fā)出警告:可以用來飲水了!在這里洗澡模式和飲用模式扮演了監(jiān)聽的角色,而熱水器則是被監(jiān)聽的對象。一旦熱水器中的水溫度發(fā)生變化,監(jiān)聽者就能及時(shí)知道并做出相應(yīng)的判斷和動(dòng)作。
??UML類圖如下:
監(jiān)聽模式的類圖Python代碼如下:
#?-*-?coding:?utf-8?-*-
from?abc?import?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,?object=0):
????????for?o?in?self.__observers:
????????????o.update(self,?object)
#?熱水器
class?WaterHeater(Observable):
????def?__init__(self):
????????super().__init__()
????????self.__temperature?=?25
????def?getTemperature(self):
????????return?self.__temperature
????def?setTemperature(self,?temperature):
????????self.__temperature?=?temperature
????????print(f"當(dāng)前溫度是:{str(self.__temperature)}℃")
????????self.notifyObservers()
#?洗澡模式
class?WashingMode(Observer):
????def?update(self,?observable,?object):
????????if?isinstance(observable,?WaterHeater)?and?50?<=?observable.getTemperature()?<=?70:
????????????print("水已燒好!溫度正好,可以用來洗澡了。")
#?飲用模式
class?DrinkingMode(Observer):
????def?update(self,?observable,?object):
????????if?isinstance(observable,?WaterHeater)?and?observable.getTemperature()?>=?100:
????????????print("水以燒開!可以用來飲用了。")
def?testWaterHeater():
????heater?=?WaterHeater()
????washingObser?=?WashingMode()
????drinkingObser?=?DrinkingMode()
????heater.addObserver(washingObser)
????heater.addObserver(drinkingObser)
????heater.setTemperature(40)
????heater.setTemperature(60)
????heater.setTemperature(100)
if?__name__?==?'__main__':
????testWaterHeater()
輸出結(jié)果如下:
當(dāng)前溫度是:40℃
當(dāng)前溫度是:60℃
水已燒好!溫度正好,可以用來洗澡了。
當(dāng)前溫度是:100℃
水以燒開!可以用來飲用了。
總結(jié)
??在設(shè)計(jì)監(jiān)聽模式的程序時(shí)需要注意以下幾點(diǎn):
-
要明確誰是觀察者誰是被觀察者,只要明白誰是應(yīng)該關(guān)注的對象,問題也就明白了。一般觀察者與被觀察者之間是多對一的關(guān)系,一個(gè)被觀察對象可以有多個(gè)監(jiān)聽對象(觀察者)。
-
Observable在發(fā)送廣播通知的時(shí)候,無須指定具體的Observer,Observer可以自己決定是否訂閱Subject的通知。
-
被觀察者至少需要有三個(gè)方法:添加監(jiān)聽者、移除監(jiān)聽者、通知Observer的方法。觀察者至少要有一個(gè)方法:更新方法,即更新當(dāng)前的內(nèi)容,做出相應(yīng)的處理。
-
添加監(jiān)聽者和移除監(jiān)聽者在不同的模型稱謂中可能會(huì)有不同命名。
