【转】第01课:生活中的监听模式——一坑爹的热水器
-
-
- 用程序來模擬生活
- 從劇情中思考監聽模式
- 監聽模式
- 監聽模式的模型抽象
- 代碼框架
- 類圖
- 基于框架的實現
- 模型說明
- 設計要點
- 推模型和拉模型
- 應用場景
-
【故事劇情】
剛剛大學畢業的 Tony 只身來到北京這個碩大的城市,開始了北漂的生活。但剛剛畢業的他身無絕技、包無分文,為了生活只能住在沙河鎮一個偏僻的村子里,每天坐著程序員專線(13號線)來回穿梭于昌平區與西城區……
在一個寒冷的冬天,下班之后要坐2個小時的地鐵+公交才能回到住處,Tony 拖著疲憊的身體回到家。準備洗一個熱水澡暖暖身體,耐何簡陋的房子中用的還是90年代的熱水器。因為熱水器沒有警報更沒有自動切換模式的功能,所以燒熱水必須得守著;不然時間長了燙死豬,時間短了又冷成狗。無奈的 Tony 背靠著墻,頭望著天花板,深夜中做起了白日夢:一定要努力工作,過兩個月我就可以自己買一個智能熱水器了:水燒好了就發一個警報,我就可以直接去洗操。還要能自己設定模式,既可以燒開了用來喝,可以燒暖了用來洗……
用程序來模擬生活
Tony 陷入白日夢中……他的夢雖然在現實世界里不能立即實現,但在程序世界里可以。程序來源于生活,下面我們就用代碼來模擬 Tony 的白日夢。
源碼示例:
class WaterHeater:
"熱水器:戰勝寒冬的有利武器"
def __init__(self):
self.__observers = []
self.__temperature = 25
def getTemperature(self):
return self.__temperature
def setTemperature(self, temperature):
self.__temperature = temperature
print("current temperature is:", self.__temperature)
self.notifies()
def addObserver(self, observer):
self.__observers.append(observer)
def notifies(self):
for o in self.__observers:
o.update(self)
class Observer:
"洗澡模式和飲用模式的父類"
def update(self, waterHeater):
pass
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("水已燒開!可以用來飲用了。")
測試代碼:
def testWaterHeater():
heater = WaterHeater()
washingObser = WashingMode()
drinkingObser = DrinkingMode()
heater.addObserver(washingObser)
heater.addObserver(drinkingObser)
heater.setTemperature(40)
heater.setTemperature(60)
heater.setTemperature(100)
輸出結果:
current temperature is: 40
current temperature is: 60
水已燒好,溫度正好!可以用來洗澡了。
current temperature is: 100
水已燒開!可以用來飲用了。
從劇情中思考監聽模式
這個代碼非常簡單,水燒到50-70度時,會發出警告:可以用來洗澡了!燒到100度也會發出警告:可以用來喝了!在這里洗澡模式和飲用模式扮演了監聽的角色,而熱水器則是被監聽的對象。一旦熱水器中的水溫度發生變化,監聽者都能及時知道并做出相應的判斷和動作。其實這就是程序設計中監聽模式的生動展現。
監聽模式
監聽模式又名觀察者模式,顧名思意就是觀察與被觀察的關系,比如你在燒開水得時時看著它開沒開,你就是觀察者,水就是被觀察者;再比如說你在帶小孩,你關注她是不是餓了,是不是喝了,是不是撒尿了,你就是觀察者,小孩就是被觀察者。
觀察者模式是對象的行為模式,又叫發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。當你看這些模式的時候,不要覺得陌生,它們就是觀察者模式。
觀察者模式一般是一種一對多的關系,可以有任意個(一個或多個)觀察者對象同時監聽某一個對象。監聽的對象叫觀察者(后面提到監聽者,其實就指觀察者,兩者是等價的),被監聽的對象叫被觀察者(Observable,也叫主題 Subject)。被觀察者對象在狀態或內容發生變化時,會通知所有觀察者對象,使它們能夠做出相應的變化(如自動更新自己的信息)。
監聽模式的模型抽象
代碼框架
上面的示例代碼還是相對比較粗糙,我們可以對它進行進一步的重構和優化,抽象出監聽模式的框架模型。
class Observer:
"觀察者的基類"
def update(self, observer, 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)
類圖
上面的代碼框架可用類圖表示如下:
addObserver,removeObserver 分別用于添加和刪除觀察者,notifyObservers 用于內容或狀態變化時通知所有的觀察者。因為 Observable 的 notifyObservers 會調用 Observer 的 update 方法,所有觀察者不需要關心被觀察的對象什么時候會發生變化,只要有變化就是自動調用 update,只需要關注 update 實現就可以了。
基于框架的實現
有了上面的代碼框架之后,我們要實現示例代碼的功能就會更簡單了。最開始的示例代碼我們假設它為 version 1.0,那么再看看基于框架的 version 2.0 吧。
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("current temperature is:", self.__temperature)
self.notifyObservers()
class WashingMode(Observer):
"該模式用于洗澡用"
def update(self, observable, object):
if isinstance(observable,
WaterHeater) and observable.getTemperature() >= 50 and observable.getTemperature() < 70:
print("水已燒好,溫度正好!可以用來洗澡了。")
class DrinkingMode(Observer):
"該模式用于飲用"
def update(self, observable, object):
if isinstance(observable, WaterHeater) and observable.getTemperature() >= 100:
print("水已燒開!可以用來飲用了。")
測試代碼不用變。自己跑一下,會發現輸出結果和之前的是一樣的。
模型說明
設計要點
在設計觀察者模式的程序時要注意以下幾點:
推模型和拉模型
觀察者模式根據其側重的功能還可以分為推模型和拉模型。
推模型:被觀察者對象向觀察者推送主題的詳細信息,不管觀察者是否需要,推送的信息通常是主題對象的全部或部分數據。一般這種模型的實現中,會把被觀察者對象中的全部或部分信息通過 update 的參數傳遞給觀察者 [update(Object obj) ,通過 obj 參數傳遞]。
如某應用 App 的服務要在凌晨1:00開始進行維護,1:00-2:00期間所有服務將會暫停,這里你就需要向所有的 App 客戶端推送完整的通知消息:“本服務將在凌晨1:00開始進行維護,1:00-2:00期間所有服務將會暫停,感謝您的理解和支持!” 不管用戶想不想知道,也不管用戶會不會在這段期間去訪問,消息都需要被準確無誤地通知到。這就是典型的推模型的應用。
拉模型:被觀察者在通知觀察者的時候,只傳遞少量信息。如果觀察者需要更具體的信息,由觀察者主動到被觀察者對象中獲取,相當于是觀察者從被觀察者對象中拉數據。一般這種模型的實現中,會把被觀察者對象自身通過 update 方法傳遞給觀察者 [update(Observable observable ),通過 observable 參數傳遞 ],這樣在觀察者需要獲取數據的時候,就可以通過這個引用來獲取了。
如某應用 App 有新的版本推出,則需要發送一個版本升級的通知消息,而這個通知消息只會簡單地列出版本號和下載地址,如果你需要升級你的 App 還需要調用下載接口去下載安裝包完成升級。這其實也可以理解成是拉模型。
推模型和拉模型其實更多的是語義和邏輯上的區別。我們上面的代碼框架,從接口 [update(self, observer, object)] 上你應該知道是可以同時支持推模型和拉模型的。推模型時,observer 可以傳空,推送的信息全部通常 object 傳遞;拉模型時,observer 和 object 都傳遞數據,或只傳遞 observer,需要更具體的信息時通過 observer 引用去取數據。
應用場景
學習設計模式,更應該領悟其設計思想,不應該應該局限于代碼的層面。 觀察者模式還可以用于網絡中的客戶端和服務器,比如手機中的各種 App 的消息推送,服務端是被觀察者,各個手機 App 是觀察者,一旦服務器上的數據(如 App 升級信息)有更新,就會被推送到手機客戶端。在這個應用中你會發現服務器代碼和 App 客戶端代碼其實是兩套完全不一樣的的代碼,它們是通過網絡接口進行通迅的,所以如果你只是停留在代碼層面是無法理解的!
總結
以上是生活随笔為你收集整理的【转】第01课:生活中的监听模式——一坑爹的热水器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 直播:今晚看超级月亮 “白玉盘”高悬于空
- 下一篇: 【转】!Dynamics 365 Onl