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