1. 程式人生 > >設計模式學習——代理模式(Proxy Pattern)之 強制代理(強校驗,防繞過)

設計模式學習——代理模式(Proxy Pattern)之 強制代理(強校驗,防繞過)

arr cnblogs 其他 測試 auth using color ref use

上周溫習了代理模式: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)之 強制代理(強校驗,防繞過)