Proxy 代理模式,爲其它對象提供一種代理以控制這個對象的訪問。客戶代碼與代理類打交道,而做實際工作的類隱藏在代理類背後。當調用代理類中的一個函數時,代理類僅轉而去調用實現中相應的函數。
在需要用比較通用和複雜的對象指針代替簡單的指針的時候,使用 Proxy 代理模式。Proxy 適用於以下情況:
1.遠程代理(Remote Proxy)爲一個對象在不同的地址空間提供局部代表。比如要操作一個網絡上的一個對象(網絡性能不好時,問題尤其突出),這時應該將這個問題交給代理去完成。
2.虛代理(Virtual Proxy)根據需要創建開銷很大的對象,將這個創建的過程交給代理去完成。
3.保護代理(Protection Proxy)控制對原始對象的訪問。保護代理用於對象應該有不同的訪問權限的時候。
4.智能指引(Smart Reference)取代了簡單的指針,它在訪問對象時執行一些附加操作。主要用途包括:1)對指向實際對象的引用計數,這樣當該對象沒有引用時,可以自動釋放它(也稱智能指針 Smart Pointer)。2)當第一次引用一個持久對象時,將它裝入內存。3)在訪問一個實際對象前,檢查是否已經鎖定了它,以確保其它對象不能改變它。
Proxy 代理模式的通用結構如下:
參與者:
Proxy:保存一個引用使得代理可以訪問實體。若 RealSubject 和 Subject 的接口相同,Proxy 會引用 Subject。提供一個與 Subject 的接口相同的接口,這樣代理就可以用來代替實體。控制對實體的存取,並可能負責創建和刪除它。其它功能依賴於代理的類型:遠程代理,負責對請求及其參數進行編碼,並向不同地址空間中的實體發送已編碼的請求。虛代理,可以緩衝實體的附加信息,以便延遲對它的訪問。保護代理,檢查調用者是否有實現一個請求所必需的訪問權限。
Subject:定義 RealSubject 和 Proxy 的共用接口,這樣就在任何使用 RealSubject 的地方都可以使用 Proxy。
RealSubject:定義 Proxy 所代表的實體。
Proxy 代理根據其種類,在適當的時候向 RealSubject 轉發請求。
效果:Proxy 模式在訪問對象時引入了一定程度的間接性。Remote Proxy 可以隱藏一個對象存在於不同地址空間的事實。Virtual Proxy 可以進行最優化,例如根據需要創建對象。Protection Proxy 和 Smart Reference 都允許在訪問一個對象時有一些附加的內務處理。
Proxy 模式還可以對用戶隱藏“寫時拷貝:copy-on-wirte”的優化方式,該優化根據需要創建對象。在實現 copy-on-write 時必須對實體進行引用計數。拷貝代理僅會增加引用計數。只有當用戶請求一個修改該實體的操作時,代理纔會真正拷貝它。在這種情況下,代理還必須減少實體的引用計數。當引用的數目爲零時,這個實體將被刪除。Copy-on-Write 可以大幅度的降低拷貝龐大實體時的開銷。
Proxy 代理模式示例代碼:
1:
2: #pragma once
3: #include <iostream>
4:
5: // 定義了 Proxy 和 RealSubject 的公有接口,
6: // 這樣就可以在任何需要使用到 RealSubject 的地方都使用 Proxy.
7: class Subject
8: {
9: public:
10: Subject(){}
11: virtual ~Subject(){}
12:
13: virtual void Request() = 0;
14: };
15:
16: // 真正使用的實體
17: class RealSubject : public Subject
18: {
19: public:
20: RealSubject()
21: {
22: std::cout << "Constructing a RealSubjectc." << std::endl;
23: }
24: virtual ~RealSubject(){}
25:
26: virtual void Request()
27: {
28: std::cout << "Request By RealSubject." << std::endl;
29: }
30: };
31:
32: // 代理類,含有一個指向RealSubject對象的指針
33: class Proxy : public Subject
34: {
35: public:
36: Proxy() : m_pRealSubject(NULL)
37: {
38: std::cout << "Constructing a Proxy." << std::endl;
39: }
40: virtual ~Proxy()
41: {
42: delete m_pRealSubject;
43: m_pRealSubject = NULL;
44: }
45:
46: virtual void Request()
47: {
48: // 需要使用RealSubject的時候纔去初始化
49: if (NULL == m_pRealSubject)
50: {
51: std::cout << "Request By Proxy." << std::endl;
52: m_pRealSubject = new RealSubject();
53: }
54: m_pRealSubject->Request();
55: }
56:
57:
58: private:
59: RealSubject* m_pRealSubject;
60: };
//test
1:
2: #include "Proxy.h"
3:
4: int main()
5: {
6: Subject* pProxy = new Proxy();
7: pProxy->Request();
8:
9: delete pProxy;
10:
11: return 0;
12: }
設計模式一書給出的代碼示例:
虛代理示例,根據需要創建大對象時使用
1: //虛代理示例:
2: class Image;
3: extern Image* LoadAnImageFile(const char*);
4:
5: //ImagePtr 相當於 Proxy
6: class ImagePtr
7: {
8: public:
9: ImagePtr(const char* imageFile)
10: : _imageFile(imageFile), _image(0)
11: { }
12: virtual ~ImagePtr();
13:
14: //重載 -> 和 * 運算符使用 LoadImage 將 _image 返回給它的調用者。
15: virtual Image* operator->()
16: {
17: return LoadImage(); /////////////////////////////////
18: }
19: virtual Image& operator*()
20: {
21: return *LoadImage(); //////////////////////////////////
22: }
23:
24: private:
25: Image* LoadImage()
26: {
27: if(_image = 0)
28: _image = LoadAnImageFile(_imageFile);
29: return _image;
30: }
31: private:
32: Image* _image;
33: const char* _imageFile;
34: };
35:
36: //該方法使你能夠通過 ImagePtr 對象調用 Image 操作,而省去了把這些操作作爲
37: //ImagePtr 接口的一部分的麻煩。
38: ImagePtr image = ImagePtr("anImageFileName");
39: image->Draw(Point(50,100));
40: //即 (image.operator->())->Draw(Point(50,100));
41: //請注意這裏的 image 代理起到一個指針的作用,但並沒有將它定義一個指向 Image 的指針。
42: //這意味着你不能把它當作一個真正的指向 Image 的指針來使用。因此在使用此方法時用戶
43: //應區別對待 Image 對象和 ImagePtr 對象。