設計模式學習——代理模式(Proxy Pattern)之 強制代理(強校驗,防繞過)
上周溫習了代理模式:http://www.cnblogs.com/chinxi/p/7354779.html
在此進行拓展,學習強制代理。但是發現網上大多例子都有個“天坑”(我是這麽認為的),在得到代理類之後,真實對象也可以使用自己的方法,繞過了代理,這樣使得代理沒有了意義。
關於強制代理,字面上意思很清晰,即:
1、只能通過真實對象獲取代理類來訪問方法
2、其他方法比如直接new 一個代理類 訪問方法(不通過本體獲取),或是自己通過本體直接調用,都不行
網絡上搜到的例子大多是這樣的:
1、真實對象有個私有成員,this.proxy,類型為基類的指針
2、真實對象中有方法,在最開始先判斷是否為代理
3、判斷的方法也很簡單,即判斷this.proxy是否為空
4、get_proxy的方法也很簡單,new出一個代理類賦值給thix.proxy,然後return
網絡上搜到的大多數例子是這樣的:先用真實對象直接訪問方法,再用不通過真實對象得到的代理類訪問方法,最後用get_proxy得到的代理類,只有最後一次成功了。乍一看似乎符合強制代理,但細細推敲後發現,至少少了一種情況,就是在通過真實對象得到代理之後,真實對象也可以訪問自己的方法了。原因很簡單,判斷是否為代理的方法,只是判斷this.proxy是否為空,而在get_proxy中,已經給this.proxy賦值,此時它非空,真實對象自然可以繞過代理,使用方法了。
用上周的例子,與網絡上搜到的方式就是:
車站出了新政策,自己不賣車票,但是可以通過自己想買什麽票,得知要去哪裏買(代理)。
在上周的類圖上做了修改,去掉了Tickets的派生類(簡單點....),與Proxy中獨有的方法。在此,不把get_proxy方法寫進基類,由派生類決定自己是否需要代理。
代理實現,也先用判斷_proxy是否為空。最後會有修改版本。
1 /// 2 /// @file Selling_Tickets.h 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:35:28 5 /// 6 7 #ifndef __SELLING_TICKETS_H__8 #define __SELLING_TICKETS_H__ 9 10 #include <iostream> 11 12 namespace marrs{ 13 14 using std::cout; 15 using std::endl; 16 using std::string; 17 18 class SellingTickets 19 { 20 public: 21 virtual ~SellingTickets(){} 22 23 public: 24 virtual void Selling() = 0; 25 virtual void Price() = 0; 26 27 }; 28 29 } 30 31 #endif // __SELLING_TICKETS_H__
1 /// 2 /// @file Tickets.h 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:39:17 5 /// 6 7 #ifndef __TICKETS_H__ 8 #define __TICKETS_H__ 9 10 #include "Selling_Tickets.h" 11 12 namespace marrs{ 13 class Proxy; 14 class Tickets 15 : public SellingTickets 16 { 17 public: 18 Tickets(string ticket_type); 19 public: 20 void Selling(); 21 void Price(); 22 23 public: 24 Proxy * Get_Proxy(); 25 26 private: 27 bool Is_Proxy(); 28 29 private: 30 Proxy * _proxy; 31 string _ticket_type; 32 }; 33 34 } 35 36 #endif // __TICKETS_H__
1 /// 2 /// @file Proxy.h 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:46:13 5 /// 6 7 #ifndef __PROXY_H__ 8 #define __PROXY_H__ 9 10 #include "Selling_Tickets.h" 11 12 namespace marrs{ 13 class Tickets; 14 class Proxy 15 : public SellingTickets 16 { 17 public: 18 Proxy(Tickets * ticket); 19 20 public: 21 void Selling(); 22 void Price(); 23 24 private: 25 Tickets * _ticket; 26 27 }; 28 29 } 30 31 #endif // __PROXY_H__
1 /// 2 /// @file Tickets.cc 3 /// @author marrs([email protected]) 4 /// @date 2017-08-19 14:38:00 5 /// 6 7 #include "Tickets.h" 8 #include "Proxy.h" 9 10 namespace marrs{ 11 12 Tickets::Tickets(string ticket_type) 13 : _ticket_type(ticket_type) 14 { 15 16 } 17 18 void Tickets::Selling() 19 { 20 if(Is_Proxy()) 21 { 22 cout << "sell: " << _ticket_type << endl; 23 } 24 } 25 26 void Tickets::Price() 27 { 28 if(Is_Proxy()) 29 { 30 cout << "price: 100 RMB" << endl; 31 } 32 } 33 34 Proxy * Tickets::Get_Proxy() 35 { 36 if(!_proxy) 37 { 38 _proxy = new Proxy(this); 39 } 40 return _proxy; 41 } 42 43 bool Tickets::Is_Proxy() 44 { 45 if(!_proxy) 46 { 47 cout << "please use proxy" << endl; 48 return false; 49 } 50 return true; 51 } 52 53 }
1 /// 2 /// @file Proxy.cc 3 /// @author marrs([email protected]) 4 /// @date 2017-08-19 14:52:18 5 /// 6 7 #include "Proxy.h" 8 #include "Tickets.h" 9 10 namespace marrs{ 11 12 Proxy::Proxy(Tickets * ticket) 13 : _ticket(ticket) 14 { 15 } 16 17 void Proxy::Selling() 18 { 19 _ticket->Selling(); 20 } 21 22 void Proxy::Price() 23 { 24 _ticket->Price(); 25 } 26 27 }
現在,先用前面搜到的例子進行測試:
1 /// 2 /// @file Student.cc 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:51:42 5 /// 6 7 #include "Proxy.h" 8 #include "Tickets.h" 9 10 using namespace marrs; 11 12 int main() 13 { 14 Tickets * ticket = new Tickets("bus_ticket"); 15 ticket->Price(); 16 ticket->Selling(); 17 18 Proxy * proxy = new Proxy(ticket); 19 proxy->Price(); 20 proxy->Selling(); 21 delete proxy; 22 23 proxy = ticket->Get_Proxy(); 24 proxy->Price(); 25 proxy->Selling(); 26 delete proxy; 27 28 delete ticket; 29 30 return 0; 31 }
編譯,運行:
[[email protected] ~/object-oriented/Proxy_Pattern_2]$>g++ * -o main.exe [[email protected] ~/object-oriented/Proxy_Pattern_2]$>./main.exe please use proxy please use proxy please use proxy please use proxy price: 100 RMB sell: bus_ticket
看著像是強制代理了。好,現在修改一下main:
1 /// 2 /// @file Student.cc 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:51:42 5 /// 6 7 #include "Proxy.h" 8 #include "Tickets.h" 9 10 using namespace marrs; 11 12 int main() 13 { 14 Tickets * ticket = new Tickets("bus_ticket"); 15 Proxy * proxy = ticket->Get_Proxy(); 16 Proxy * proxy_other = new Proxy(ticket); 17 18 proxy->Price(); 19 proxy->Selling(); 20 21 ticket->Price(); 22 ticket->Selling(); 23 24 proxy_other->Price(); 25 proxy_other->Selling(); 26 27 delete proxy; 28 delete ticket; 29 30 return 0; 31 }
[[email protected] ~/object-oriented/Proxy_Pattern_2]$>g++ * -o main.exe [[email protected] ~/object-oriented/Proxy_Pattern_2]$>./main.exe price: 100 RMB sell: bus_ticket price: 100 RMB sell: bus_ticket price: 100 RMB sell: bus_ticket
結果完全符合預期,真實對象也可以使用自己的方法了。甚至亂套了,隨便來個代理都可以用了。
於是,我對其進行修改,對判斷是不是proxy加了點東西:
version 1:
此方法設置了唯一代理
1 /// 2 /// @file Tickets.h 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:39:17 5 /// 6 7 #ifndef __TICKETS_H__ 8 #define __TICKETS_H__ 9 10 #include "Selling_Tickets.h" 11 12 namespace marrs{ 13 class Proxy; 14 class Tickets 15 : public SellingTickets 16 { 17 public: 18 Tickets(string ticket_type); 19 ~Tickets(); 20 public: 21 void Selling(Proxy * proxy); 22 void Price(Proxy * proxy); 23 24 private: 25 void Selling(); 26 void Price(); 27 28 public: 29 Proxy * Get_Proxy(); 30 31 private: 32 bool Is_Proxy(Proxy * proxy) const; 33 34 private: 35 string _ticket_type; 36 Proxy * _proxy; 37 }; 38 39 } 40 41 #endif // __TICKETS_H__
1 /// 2 /// @file Proxy.h 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:46:13 5 /// 6 7 #ifndef __PROXY_H__ 8 #define __PROXY_H__ 9 10 #include "Selling_Tickets.h" 11 12 namespace marrs{ 13 class Tickets; 14 class Proxy 15 : public SellingTickets 16 { 17 public: 18 Proxy(Tickets * ticket); 19 20 public: 21 void Selling(); 22 void Price(); 23 24 private: 25 Tickets * _ticket; 26 27 }; 28 29 } 30 31 #endif // __PROXY_H__
1 /// 2 /// @file Tickets.cc 3 /// @author marrs([email protected]) 4 /// @date 2017-08-19 14:38:00 5 /// 6 7 #include "Tickets.h" 8 #include "Proxy.h" 9 10 namespace marrs{ 11 12 Tickets::Tickets(string ticket_type) 13 : _ticket_type(ticket_type) 14 , _proxy(NULL) 15 { 16 17 } 18 19 Tickets::~Tickets() 20 { 21 if(_proxy) 22 { 23 delete _proxy; 24 } 25 } 26 27 void Tickets::Selling(Proxy * proxy) 28 { 29 if(Is_Proxy(proxy)) 30 { 31 Selling(); 32 } 33 } 34 35 void Tickets::Price(Proxy * proxy) 36 { 37 if(Is_Proxy(proxy)) 38 { 39 Price(); 40 } 41 } 42 43 void Tickets::Selling() 44 { 45 cout << "sell: " << _ticket_type << endl; 46 } 47 48 void Tickets::Price() 49 { 50 cout << "price: 100 RMB" << endl; 51 } 52 53 Proxy * Tickets::Get_Proxy() 54 { 55 if(!_proxy) 56 { 57 _proxy = new Proxy(this); 58 return _proxy; 59 } 60 return NULL; 61 } 62 63 bool Tickets::Is_Proxy(Proxy * proxy) const 64 { 65 if(proxy != _proxy) 66 { 67 cout << "please use proxy" << endl; 68 return false; 69 } 70 return true; 71 } 72 73 }
1 /// 2 /// @file Proxy.cc 3 /// @author marrs([email protected]) 4 /// @date 2017-08-19 14:52:18 5 /// 6 7 #include "Proxy.h" 8 #include "Tickets.h" 9 10 namespace marrs{ 11 12 Proxy::Proxy(Tickets * ticket) 13 : _ticket(ticket) 14 { 15 } 16 17 void Proxy::Selling() 18 { 19 _ticket->Selling(this); 20 } 21 22 void Proxy::Price() 23 { 24 _ticket->Price(this); 25 } 26 27 }
1 /// 2 /// @file Student.cc 3 /// @author marrs([email protected]) 4 /// @date 2017-08-13 20:51:42 5 /// 6 7 #include "Proxy.h" 8 #include "Tickets.h" 9 10 using namespace marrs; 11 12 int main() 13 { 14 Tickets * ticket = new Tickets("bus_ticket"); 15 Proxy * proxy = ticket->Get_Proxy(); 16 17 proxy->Price(); 18 proxy->Selling(); 19 20 Proxy * proxy_other = new Proxy(ticket); 21 proxy_other->Price(); 22 proxy_other->Selling(); 23 24 ticket->Price(proxy); 25 ticket->Selling(proxy); 26 27 ticket->Price(proxy_other); 28 ticket->Selling(proxy_other); 29 30 delete proxy_other; 31 delete ticket; 32 33 return 0; 34 }
1 [[email protected] ~/object-oriented/Proxy_Pattern_3]$>g++ *.h *.cc -o main.exe 2 [[email protected] ~/object-oriented/Proxy_Pattern_3]$>./main.exe 3 price: 100 RMB 4 sell: bus_ticket 5 please use proxy 6 please use proxy 7 price: 100 RMB 8 sell: bus_ticket 9 please use proxy 10 please use proxy
這樣的話,還有點小問題,就是真實對象可以通過傳入代理的指針來訪問自己的方法。但是,如果不傳參的話,是用不了的。基類的那兩個方法,在Ticket中,寫進了private。還有,此方法目前缺少一個回收代理的方法。萬一另一個地方要用的話,就用不了了。
version 2
version 2 其實就是把傳參改成了隨機字符串,此字符串在get_proxy中生成,並傳入Proxy對象中,只有代理和真實對象知道那是什麽。這裏就不實現了,跟version 1 差不多的。
version 3
多個代理
此方法只不過是在真實對象Ticket中增加私有成員map<Proxy * , int> ,用來存儲自己的多個代理,為version 2 的多代理版本。此處也不實現了。
version 4
使用引用計數
此方法也是version 2 的多代理版本,增加引用計數。計數歸0時回收代理對象。
註:version 1 - 4都是我自己瞎想的....雖能實現,但是不知道是否實用。
設計模式學習——代理模式(Proxy Pattern)之 強制代理(強校驗,防繞過)