1.设计模式中监听模式(观察者模式)(Python实现)
目錄
?
1.什么是監(jiān)聽模式
2.監(jiān)聽模式設(shè)計(jì)思想
3.監(jiān)聽模式的框架模型
4.分別用框架模型和不用框架模型來解決下面的例子
5.模型說明
6.監(jiān)聽模式根據(jù)側(cè)重可以分為推模型和拉模型
7.賬號異常登錄檢測和診斷機(jī)制
8.應(yīng)用場景
1.什么是監(jiān)聽模式
? ? 監(jiān)聽模式又名觀察者模式,顧名思義就是觀察者與被觀察的關(guān)系。比如在燒開水的時候它開沒開,你就是觀察者,水就是被觀察者,等等像這種的模式。?
2.監(jiān)聽模式設(shè)計(jì)思想
? ?觀察者模式是對象的行為模式。
? ?監(jiān)聽模式的核心思想就是在被觀察者與觀察者之間建立一種自動觸發(fā)的關(guān)系。
? ?監(jiān)聽模式是一種一對多的關(guān)系,可以有一個或者多個觀察者對象同時監(jiān)聽某一個對象。監(jiān)聽的對象叫觀察者(監(jiān)聽者),被監(jiān)聽的對象叫被觀察者(Observable,也叫主題,即Subject)。被觀察者對象在狀態(tài)或內(nèi)容(數(shù)據(jù))發(fā)生變化時,會通知所有觀察者對象,使它們能夠做出相應(yīng)的變化(如自動更新自己的信息)
3.監(jiān)聽模式的框架模型
from abc import ABCMeta, abstractmethod# 引入ABCMeta 和 abstractmethod 來定義抽象類和抽象方法# metaclass=ABCMeta : 使創(chuàng)建的類為抽象類 class Observer(metaclass=ABCMeta):"""觀察者的基類"""@abstractmethoddef updata(self, observalbe, object):passclass Observable:"""被觀察者的基類"""def __init__(self):self.__observers = []# 添加觀察者def addObserver(self, observer):self.__observers.append(observer)# 刪除觀察者def removeObserver(self, observer):self.__observers.remove(observer)# 內(nèi)容或狀態(tài)變化時通知所有的觀察者def notifyObservers(self, object=0):for o in self.__observers:o.updata(self, object)? Observable是被觀察者的抽象類,Observer是觀察者的抽象類。? 另外還有三個方法,分別用于添加和刪除觀察者,notifyObservers?用于內(nèi)容或狀態(tài)變化時通知所有的觀察者。因?yàn)镺bservable的notifyObservers會調(diào)用Observer的update方法,所有觀察者不需要關(guān)心被觀察者的對象什么時候會發(fā)生變化,只要有變化就會自動調(diào)用update,所以只需要關(guān)注update實(shí)現(xiàn)就可以了。
4.分別用框架模型和不用框架模型來解決下面的例子
? ? 簡單的例子,比如熱水器熱水,到多少度可以飲用,到多少度可以洗澡的問題,
不用框架模型
from abc import ABCMeta, abstractmethod# 熱水器類 class WaterHeater:def __init__(self):# 觀察者的數(shù)量self.__observers = []# 設(shè)置初始溫度self.__temperature = 25# 獲取溫度def getTemperature(self):return self.__temperature# 設(shè)置溫度def setTemperature(self, temperature):self.__temperature = temperatureprint("當(dāng)前的溫度是", str(temperature), "℃")# 調(diào)用notifies方法,其實(shí)就是調(diào)用每個觀察者中的updateself.notifies()def addObserver(self, observer):self.__observers.append(observer)def notifies(self):for o in self.__observers:o.update(self, )# 創(chuàng)建觀察者的抽象類 class Observer(metaclass=ABCMeta):"""觀察者的基類"""@abstractmethoddef update(self, observalbe, object):pass# 洗澡的類WashingMode繼承父類Observer class WashingMode(Observer):# 重寫父類中靜態(tài)方法updatedef update(self, waterHeater, **kwargs):if 50 <= waterHeater.getTemperature() <= 70:print("水已經(jīng)燒好,可以洗澡")# 喝水的類DrinkingMode繼承父類Observer class DrinkingMode(Observer):# 重寫父類中靜態(tài)方法updatedef update(self, waterHeater, **kwargs):if waterHeater.getTemperature() >= 100:print("水已經(jīng)燒開,可以飲用了")def testWaterHeatper():heater = WaterHeater() # 創(chuàng)建熱水器類的對象washingObser = WashingMode() # 創(chuàng)建洗澡類的對象drinkingObser = DrinkingMode() # 創(chuàng)建喝水類的對象# 調(diào)用熱水器對象的添加觀察者(洗澡類對象)的方法heater.addObserver(washingObser)# 調(diào)用熱水器對象的添加觀察者(喝水類對象)的方法heater.addObserver(drinkingObser)# 設(shè)置各種溫度heater.setTemperature(40)heater.setTemperature(60)heater.setTemperature(100)# 調(diào)用測試方法 testWaterHeatper() """ 運(yùn)行結(jié)果: 當(dāng)前的溫度是 40 ℃ 當(dāng)前的溫度是 60 ℃ 水已經(jīng)燒好,可以洗澡 當(dāng)前的溫度是 100 ℃ 水已經(jīng)燒開,可以飲用了 """使用框架模型
# @Time : 2020/9/27 11:56 # @Author : GodWei # @File : observerPattern.pyfrom abc import ABCMeta, abstractmethod, ABC# 引入ABCMeta 和 abstractmethod 來定義抽象類和抽象方法# metaclass=ABCMeta : 使創(chuàng)建的類為抽象類 class Observer(metaclass=ABCMeta):"""觀察者的基類"""@abstractmethoddef update(self, observable, object):passclass Observable:"""被觀察者的基類"""def __init__(self):self.__observers = []# 添加觀察者def addObserver(self, observer):self.__observers.append(observer)# 刪除觀察者def removeObserver(self, observer):self.__observers.remove(observer)# 內(nèi)容或狀態(tài)變化時通知所有的觀察者def notifyObservers(self, object=0):for o in self.__observers:o.update(self, object)class WasterHeater(Observable):"""熱水器的類,繼承父類(被觀察者的抽象類)"""def __init__(self):super().__init__()self.__temperature = 25def getTemperature(self):return self.__temperaturedef setTemperature(self, temperature):self.__temperature = temperatureprint("當(dāng)前的溫度是", str(temperature), "℃")self.notifyObservers()class WashingMode(Observer):"""洗澡的類,繼承父類(觀察者的抽象類)"""def update(self, observable, object):if isinstance(observable, WasterHeater) \and 50 <= observable.getTemperature() <= 70:print("水已經(jīng)燒好,可以洗澡")class DrinkingMode(Observer):"""喝水的類,繼承父類(觀察者的抽象類)"""def update(self, observable, object):if isinstance(observable, WasterHeater) \and observable.getTemperature() >= 100:print("水已經(jīng)燒開,可以飲用")def testWaterHeatper():heater = WasterHeater() # 創(chuàng)建熱水器類的對象washingObser = WashingMode() # 創(chuàng)建洗澡類的對象drinkingObser = DrinkingMode() # 創(chuàng)建喝水類的對象# 調(diào)用熱水器對象的添加觀察者(洗澡類對象)的方法heater.addObserver(washingObser)# 調(diào)用熱水器對象的添加觀察者(喝水類對象)的方法heater.addObserver(drinkingObser)# 設(shè)置各種溫度heater.setTemperature(40)heater.setTemperature(60)heater.setTemperature(100)# 調(diào)用測試方法 testWaterHeatper() """ 運(yùn)行結(jié)果: 當(dāng)前的溫度是 40 ℃ 當(dāng)前的溫度是 60 ℃ 水已經(jīng)燒好,可以洗澡 當(dāng)前的溫度是 100 ℃ 水已經(jīng)燒開,可以飲用 """結(jié)果是一樣的,使用框架后,結(jié)構(gòu)更新清晰,直接用觀察者被觀察者的父類繼承即可
5.模型說明
? (1)要明確誰是觀察者誰是被觀察者,問題也就明白了。一般觀察者與被觀察者之間是多對一的關(guān)系,一個被觀察者對象可以有多個監(jiān)聽對象(觀察者)。
? ?(2)?被觀察者至少需要有三個方法:添加監(jiān)聽者,移除監(jiān)聽者,通知Observer的方法。觀察者至少要有一個方法:更新方法,即更新當(dāng)前的內(nèi)容,做出相應(yīng)的處理
6.監(jiān)聽模式根據(jù)側(cè)重可以分為推模型和拉模型
? ? (1)?推模型:被觀察者對象向觀察者推送主題的詳細(xì)信息,不管觀察者是否需要,推送的信息通常是主題對象的全部或部分?jǐn)?shù)據(jù)。一般在這種模型的實(shí)現(xiàn)中,會把觀察者對象中的全部或部分信息通過update參數(shù)傳遞給觀察者
? ? (2)?拉模型:?被觀察者在通知觀察者的時候,只傳遞少量信息。如果觀察者需要更具體的信息,由觀察者主動到被觀察者對象中獲取,相當(dāng)于觀察者從被觀察者對象中拉數(shù)據(jù)。一般在這種模型中,會把被觀察者對象自身通過update方法傳遞給觀察者,這樣在觀察者需要獲取數(shù)據(jù)的時候,就可以通過這個引用來獲取了。
7.賬號異常登錄檢測和診斷機(jī)制
?服務(wù)器會記錄最近登錄的時間、地區(qū)、IP地址等,從而知道常用的登錄地區(qū),如果檢測出哪一次登錄和平時的地區(qū)相差非常大,則認(rèn)為是一次異常登錄,?只要登錄異常一出現(xiàn)就自動發(fā)送信息,短信和郵箱的發(fā)送機(jī)制我們就可以認(rèn)為是登錄的觀察者。
import time from abc import ABCMeta, abstractmethod# 使用監(jiān)聽模式的框架 class Observer(metaclass=ABCMeta):"""觀察者的基類"""@abstractmethoddef update(self, observable, object):passclass Observable:"""被觀察者的基類"""def __init__(self):self.__observers = []# 添加觀察者def addObserver(self, observer):self.__observers.append(observer)# 刪除觀察者def removeObserver(self, observer):self.__observers.remove(observer)# 內(nèi)容或狀態(tài)變化時通知所有的觀察者def notifyObservers(self, object=0):for o in self.__observers:o.update(self, object)class Account(Observable):"""用戶賬戶"""def __init__(self):super().__init__()self.__latestIp = {}self.__latestRegion = {}def login(self, name, ip, time):region = self.__getRegion(ip)if self.__isLongDistance(name, region):self.notifyObservers({"name": name, "ip": ip, "region": region, "time": time})self.__latestRegion[name] = regionself.__latestIp[name] = ipdef __getRegion(self, ip):"""由Ip地址獲取地區(qū)信息,(只是進(jìn)行模擬)"""ipRegions = {"117.23.33.34": "陜西省咸陽市","66.117.31.255": "美國洛杉磯"}region = ipRegions.get(ip)if region is None:return ""else:return regiondef __isLongDistance(self, name, region):"""計(jì)算本次登錄與最近登錄的地區(qū)差距(只是簡單的進(jìn)行模擬,用字符串匹配模擬) """latestRegion = self.__latestRegion.get(name)if latestRegion is not None and latestRegion != region:return latestRegionclass SmsSender(Observer):"""短信發(fā)送器"""def update(self, observable, object):print("【短信發(fā)送】" + object["name"] + "您好!系統(tǒng)檢測到您的賬戶可能登錄異常。最近一次登錄信息:\n"+ " 登錄地區(qū):" + object["region"] + " 登錄ip :" + object["ip"] +" 登錄時間:" + time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(object["time"])))class MailSender(Observer):"""郵件發(fā)送器"""def update(self, observable, object):print("【郵件發(fā)送】" + object["name"] + "您好!系統(tǒng)檢測到您的賬戶可能登錄異常。最近一次登錄信息:\n"+ " 登錄地區(qū):" + object["region"] + " 登錄ip :" + object["ip"] +" 登錄時間:" + time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(object["time"])))def testLogin():accout = Account()accout.addObserver(SmsSender())accout.addObserver(MailSender())accout.login("GodWei ", "117.23.33.34", time.time())accout.login("GodWei ", "66.117.31.255", time.time())testLogin()運(yùn)行結(jié)果:
""" 【短信發(fā)送】GodWei 您好!系統(tǒng)檢測到您的賬戶可能登錄異常。最近一次登錄信息:登錄地區(qū):美國洛杉磯 登錄ip :66.117.31.255 登錄時間:2020-09-28 09:21:25 【郵件發(fā)送】GodWei 您好!系統(tǒng)檢測到您的賬戶可能登錄異常。最近一次登錄信息:登錄地區(qū):美國洛杉磯 登錄ip :66.117.31.255 登錄時間:2020-09-28 09:21:25 """? ?這段代碼只是用來簡單模擬程序,而且只記錄了上一次的登錄信息到Account對象中
? ?實(shí)際項(xiàng)目中,像用戶的信息,登錄時間,ip,最近登錄的情況,都存在數(shù)據(jù)庫中。
8.應(yīng)用場景
? (1)?對一個對象狀態(tài)或數(shù)據(jù)的更新需要其他對象同步更新,或者一個對象的更新需要依賴另一個對象的更新
? (2)?對象僅需要將自己的更新通知給其他對象而不需要知道其他對象的細(xì)節(jié),如消息推送
總結(jié)
以上是生活随笔為你收集整理的1.设计模式中监听模式(观察者模式)(Python实现)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python中的并行处理(Pool.ma
- 下一篇: 1.Python算法之枚举算法