結構型——代理模式
代理模式指的是通過創建一個代理來控制對原始對象的訪問。代理在客戶端與實際對象之間充當“中介”
特點
- 訪問控制:代理對象可以控制對實際對象的訪問,從而實現對訪問權限的控制。
- 延遲加載:代理對象可以在實際對象被調用時才創建,從而實現延遲加載。
- 解耦: 客戶端金依賴代理接口,可以與實際對象分離,從而實現解耦。
- 類型多樣:虛擬代理(既延遲加載)、緩存代理(緩存對象)、遠程代理(遠程對象)、保護代理(權限控制)等。
模式結構
角色 | 描述 |
---|
抽象主題 (Subject) | 聲明真實主題和代理主題的共同接口。客戶端依賴此接口 |
真實主題 (RealSubject) | 實現抽象主題定義的接口,并提供其功能實現。實際執行業務邏輯的對象 |
代理主題 (Proxy) | 實現抽象主題定義的接口,并持有真實主題的引用。代理主題控制對真實主題的訪問 |
代理模式類型
類型 | 應用場景 |
---|
虛擬代理 | 當需要創建一個大開銷的對象時,創建一個代理對象,直到需要時才創建真實的對象。 也叫延遲加載(比如在圖片加載時,先加載一張占位圖,當圖片加載完成后再替換占位圖)。 |
保護代理 | 控制權限(在客戶端對真實對象進行訪問前,利用代理進行權限校驗等操作)。 |
緩存代理 | 緩存調用結果(為優化性能,將重復計算的值緩存起來,下次使用時直接返回緩存值)。 |
遠程代理 | 本地代理遠程服務(將本地的請求轉發到遠程的對象上)。 |
簡單示例
1. 虛擬代理
from abc import ABC, abstractmethod
class Image(ABC):@abstractmethoddef display(self):pass
class RealImage(Image):def __init__(self, filename):self.filename = filenameself.load_from_disk()def load_from_disk(self):print(f"Loading {self.filename} from disk")def display(self):print(f"Displaying {self.filename}")
class ProxyImage(Image):def __init__(self, filename):self.filename = filenameself.real_image = None def display(self):if not self.real_image:self.real_image = RealImage(self.filename)self.real_image.display()
if __name__ == "__main__":proxy_image = ProxyImage("test_image.jpg") proxy_image.display()
2. 保護代理
from abc import ABC, abstractmethod
class DataBase(ABC):@abstractmethoddef query(self, sql):pass
class RealDataBase(DataBase):def query(self, sql):return f"query result: {sql}"
class ProxyDataBase(DataBase):def __init__(self, user):self.user = userself.real_db = RealDataBase()def query(self, sql):if self._check_access():return self.real_db.query(sql)else:return "access denied"def _check_access(self):return self.user == "admin"
if __name__ == "__main__":proxy_db = ProxyDataBase("admin")print(proxy_db.query("select * from user")) proxy_db = ProxyDataBase("guest")print(proxy_db.query("select * from user"))
3. 緩存代理
from abc import ABC, abstractmethodclass Calculate(ABC):@abstractmethoddef calculate(self, n): passclass RealCalculate(Calculate):def calculate(self, n):return n * nclass ProxyCalculate(Calculate):def __init__(self):self.cache = {}self.real_calculate = RealCalculate()def calculate(self, n):if n not in self.cache:self.cache[n] = self.real_calculate.calculate(n)return self.cache[n]if __name__ == '__main__':proxy_calculate = ProxyCalculate()for i in range(10):print(proxy_calculate.calculate(i)) for i in range(10):print(proxy_calculate.calculate(i))
4.遠程代理
import requestsclass BaiduService:def get_data(self, question): passclass BaiduServiceProxy(BaiduService):def __init__(self):self.question = questionself.baidu_api = "www.baidu.com"def get_data(self, question):response = requests.get(self.baidu_api, params={"question": question})return response.text
if __name__ == "__main__":baidu_service = BaiduServiceProxy()data = baidu_service.get_data("如何使用代理模式")
代理模式 VS 外觀模式 VS 裝飾器模式 VS 適配器模式
維度 | 代理模式 | 外觀模式 | 裝飾器模式 | 適配器模式 |
---|
核心目的 | 控制對象訪問 | 簡化復雜系統接口 | 動態擴展對象功能 | 接口兼容性轉換 |
結構特點 | 代理與真實對象實現相同接口 | 外觀類聚合多個子系統接口 | 裝飾器模式聚合被裝飾對象 | 適配器模式聚合被適配對象 |
對象關系 | 代理對象持有真實對象 | 外觀持有多個子系統對象 | 裝飾器包裹原始對象 | 適配器持有或繼承被適配對象 |
接口一致性 | 保持與真實對象接口一致 | 提供新的簡化接口 | 保持與原始對象接口一致 | 轉換接口以匹配目標需求 |
擴展方向 | 訪問控制邏輯 | 接口簡化與整合 | 功能疊加 | 接口適配 |