趣谈设计模式 | 代理模式(Proxy):利用代理来控制对象的访问
文章目錄
- 案例:房屋中介
- 代理模式
- 代理模式與裝飾器模式
- 代理模式的應用
- 遠程代理
- 虛擬代理
- 安全代理
- 智能引用代理
- 寫時拷貝代理
- 總結
- 完整代碼與文檔
由于代理模式相較于前面的其他設計模式來說更加簡單,容易理解,所以為了保證內容不會太少,我除了介紹代理模式外還會重點介紹遠程代理和虛擬代理,以及簡單提及其他的一些代理模式的應用
案例:房屋中介
假設小明準備去外地實習,于是他需要租一間房來居住。
但是由于身在外地,人生地不熟的他根本沒辦法和正在出租房屋的房東聯系,于是他想到了一個好辦法,找到具有人脈的房產中介來代替他租房
有了房產中介的加入,小明就可以通過中介來幫助他聯系房東,進行租房。
但是此時又面臨了一個問題,此時租房的行為是由中介來代為進行的,因此在房東眼里,這間房屋是中介在租,他根本不知道小明的存在。這就導致了一個問題,如果中介翻臉不認賬獨占房屋,又或者房東有急事要通知小明,那該怎么辦呢?
小明讓中介代替他去租房,實際租房的人并不是中介,而是小明。因此我們在中介租房的時候,應該清楚的告知房東租房的對象,于是邏輯圖變化如下
以行為的角度來說,小明和中介要做的事情都是租房,所以在旁觀人眼里,他們兩個人都是尋求租房的人,因此我們可以將他們兩人都歸類為租房者
以代碼的角度來實現的話,我們可以將租房者定義為一個接口
小明也是租房者,因此他會去實現這個接口
class XiaoMing: public Tenant { public:void rentingHouse() override{std::cout << "小明需要租房!" << std::endl;} };而中介為了讓房東知道租房的真實對象,他會保留小明的個人信息(小明對象的引用),并且他除了執行小明的租房任務以外,還會附加新的邏輯(收取傭金),代碼如下
class Proxy : public Tenant { public:Proxy(Tenant* client): _client(client){}void rentingHouse() override{_client->rentingHouse(); //代理行為std::cout << "中介代替客戶去租房,并收取傭金" << std::endl; //新增的代理邏輯}private:Tenant* _client; //代理的客戶,需要讓目標知道租房的人是誰 };下面測試一下邏輯是否正確
int main() {Tenant* xiaoming = new XiaoMing(); //小明Proxy* proxy = new Proxy(xiaoming); //中介代理小明的租房行為proxy->rentingHouse(); //中介代替客戶租房delete proxy, xiaoming;return 0; }
小明雖然不認識房東,但是他通過中介幫助他租房,上面所描述的這一系列行為,其實就是代理模式
代理模式
代理模式為其他對象提供一種代理以控制(隔離,使用接口)對這個對象的訪問
代理模式由以下三部分組成
- Subject:RealSubject和Proxy的共同接口,利用多態的性質來保證RealSubject能夠隨時使用Proxy
- RealSubject:實體,業務邏輯的真實執行者,同時也是Proxy代表的對象。
- Proxy:代理,保存了實體的引用使其可以訪問實體,并且實現了Subject接口,保證其能夠隨時利用代理來替代實體,并添加新的附加功能
類圖如下
從之前所舉的例子中,我們不難看出,代理模式的意圖就是通過代理實體,來為其附加新的功能。來達到業務邏輯與附加功能的解耦
在上面的例子中,中介為小明代理后,分析需求、尋找房源、聯系房東、代替租房,這些行為全都由中介來實現,小明只需要關心租房這件事情,這樣我們就達到了解耦合的目的。
如果以工程實踐的角度來講的話,就好比程序員(實體)只需要負責必須的業務功能實現,而對于如:監控、統計、日志等附加的功能就交由運維、數據分析人員(代理)來實現。
但是如果這樣理解的話,那么代理模式豈不是和裝飾器模式一樣,都在為實體對象增加新功能嗎?
代理模式與裝飾器模式
不了解裝飾器模式的可以看看我的往期博客
趣談設計模式 | 裝飾器模式(Decorator):用裝飾來動態擴展功能
搬出裝飾器模式的類圖,我們發現它的結構其實與代理模式非常相似。
裝飾器模式使用裝飾器來包裝實體,而代理模式使用代理來代理實體,他們都是用一個對象將另一個對象包起來,并且把調用委托給實體。并且在這個基礎上,他們都為實體附加了新的行為。
但是在前面的博客中我也說過,雖然大體的結構相同,但是這并不意味這這兩個設計模式一樣,真正決定設計模式的其實是它具體的設計意圖
代理模式的核心意圖是控制對象的訪問,代理通過增加與實體類無關的附加行為來達到訪問控制的目的。
裝飾器模式的核心意圖是增強對象的功能,通過附加與實體相關的新行為的方式,來增加實體類原本的功能
代理模式的應用
下面講講遠程代理和虛擬代理,并簡單提一下安全代理、智能引用代理、寫時拷貝代理
遠程代理
遠程代理也就是為一個對象在不同的地址空間提供局部代表,這樣可以隱藏一個對象存在于不同地址空間的事實
例如我們借助代理服務器來訪問外網的時候,就是使用的遠程代理。我們將請求直接發送給代理服務器,而后代理服務器將請求轉發到真實服務器并接收服務器的響應,最后代理服務器再將響應的結果歸還給我們。這樣就仿佛我們直接與真實服務器通信一樣。
虛擬代理
虛擬代理,是根據需要創建開銷很大的對象,通過它來存放實例化需要很長時間的真實對象,來達到性能的最優化。
例如我們在網上購物的時候,通常界面上會顯示很多商品圖片,但是由于圖片過多或者圖片過大導致加載速度慢,導致圖片可能立即無法顯示出來。在這段加載的使用中 ,我們就是使用虛擬代理來替代了真實的圖片,而虛擬代理中則存儲了真實圖片的路徑
上圖中的白框即為虛擬代理,我們會先用它來暫時替代真實圖片,并且虛擬代理還會在內部中繼續加載真實圖片,加載后如下
以代碼來實現的話
下面的幾種應用比較簡單,因此就簡單描述一下
安全代理
用來控制對真實對象訪問時的權限,通常在對象有不同的訪問權限時使用
最典型的應用即防火墻,通過設定黑白名單以及訪問權限,來控制對真實對象的訪問。
智能引用代理
當真實對象被引用時,代理進行額外的動作
最典型的就是C++中的智能指針中的share_ptr,對于實體資源來說,智能指針就是它的代理。當智能指針第一次引用資源的時候,就會對其進行管理,而當智能指針的生命周期結束,沒有智能指針再引用這個資源時,就將這個資源給釋放掉。
寫時拷貝代理
延遲對象的復制,直到客戶真正需要它時
寫時拷貝代理其實就是將虛擬代理與引用計數機制相結合,最典型的就是Linux中父子進程的寫時拷貝。當父進程創建子進程時,為了避免不必要的拷貝,只有當子進程需要修改數據時,才會將去拷貝父進程的數據,否則繼續使用父進程的數據。
總結
- 代理模式為其他對象提供一種代理以控制對這個對象的訪問
- 代理模式用于控制訪問,裝飾器模式用于增強功能,兩者雖然結構相同,但是意圖不同。
- 將業務邏輯與附加功能解耦合,實體只需要關注自己部分的功能,而不用關心代理所做的事情,職責更加清晰,拓展性更高
- 在客戶端和實體之間增加代理對象,導致請求的處理速度可能會變慢
完整代碼與文檔
如果有需要完整代碼或者markdown文檔的同學可以點擊下面的github鏈接
github
總結
以上是生活随笔為你收集整理的趣谈设计模式 | 代理模式(Proxy):利用代理来控制对象的访问的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 趣谈设计模式 | 策略模式(Strate
- 下一篇: 趣谈设计模式 | 命令模式(Comman