青春总是那样,逝去了才开始回味;大学生活也是在不经意间就溜走了,现在上班的时候,偶尔还会怀念大学时,大家在一起玩游戏的时光。大学喜欢玩游戏,但是可悲的校园网,速度能把人逼疯了;还好,后来搞了一个游戏代理,总算能勉勉强强的玩了两年。时至今日,敲起键盘写设计模式的时候,又想起了那些美好的时光。好了,这是一篇技术文章,而不是抒情怀旧的散文;思绪再回到这篇文章上来,游戏代理,是个什么东西,有了它就能让我们玩游戏的延迟立马下来了。今天,我并不会去总结游戏代理是如何实现的,重点是通过游戏代理这个例子来总结设计模式中的代理模式。
在GOF的《设计模式:可复用面向对象软件的基础》一书中对代理模式是这样说的:为其他对象提供一种代理以控制对这个对象的访问。结合上面的游戏代理的例子和下面的图,我们来进行分析一下。以前你是这样玩游戏:
现在有了游戏代理,你是这样玩游戏:
代理服务器干了什么?它代替你去和游戏服务器进行交互。它访问游戏服务器的速度比你使用校园网访问游戏服务器的速度快很多。所以,你的游戏延迟就下来了。
代理模式分为四类:远程代理,虚代理,保护代理和智能引用。在下面使用场合会对这四种进行分别介绍。
Proxy
Subject:定义RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy;
RealSubject:定义Proxy所代理的实体。
上面也总结了,代理模式分为远程代理,虚代理,保护代理和智能引用这四种,而分为这四种,就是对应不同的使用场合的。
最简单的实现,对上述UML类图的直接代码体现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
#include
using
namespace
std
;
#define SAFE_DELETE(p) if (p) { delete p; p = NULL;}
class
CSubject
{
public
:
CSubject
(
)
{
}
;
virtual
~
CSubject
(
)
{
}
virtual
void
Request
(
)
=
0
;
}
;
class
CRealSubject
:
public
CSubject
{
public
:
CRealSubject
(
)
{
}
~
CRealSubject
(
)
{
}
void
Request
(
)
{
cout
<<
"CRealSubject Request"
<<
endl
;
}
}
;
class
CProxy
:
public
CSubject
{
public
:
CProxy
(
)
:
m_pRealSubject
(
NULL
)
{
}
~
CProxy
(
)
{
SAFE_DELETE
(
m_pRealSubject
)
;
}
void
Request
(
)
{
if
(
NULL
==
m_pRealSubject
)
{
m_pRealSubject
=
new
CRealSubject
(
)
;
}
cout
<<
"CProxy Request"
<<
endl
;
m_pRealSubject
->
Request
(
)
;
}
private
:
CRealSubject
*
m_pRealSubject
;
}
;
int
main
(
)
{
CSubject
*
pSubject
=
new
CProxy
(
)
;
pSubject
->
Request
(
)
;
SAFE_DELETE
(
pSubject
)
;
}
|
上面的实现,就是对代理模式的最原始体现,现在提供一个有血有肉的实际应用级的体现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
#include
#include
using
namespace
std
;
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
class
KRefCount
{
public
:
KRefCount
(
)
:
m_nCount
(
0
)
{
}
public
:
unsigned
AddRef
(
)
{
return
InterlockedIncrement
(
&m_nCount
)
;
}
unsigned
Release
(
)
{
return
InterlockedDecrement
(
&m_nCount
)
;
}
void
Reset
(
)
{
m_nCount
=
0
;
}
private
:
unsigned
long
m_nCount
;
}
;
template
<
typename
T
>
class
SmartPtr
{
public
:
SmartPtr
(
void
)
:
m_pData
(
NULL
)
{
m_pReference
=
new
KRefCount
(
)
;
m_pReference
->
AddRef
(
)
;
}
SmartPtr
(
T
*
pValue
)
:
m_pData
(
pValue
)
{
m_pReference
=
new
KRefCount
(
)
;
m_pReference
->
AddRef
(
)
;
}
SmartPtr
(
const
SmartPtr
<
T
>
&
sp
)
:
m_pData
(
sp
.
m_pData
)
,
m_pReference
(
sp
.
m_pReference
)
{
m_pReference
->
AddRef
(
)
;
}
~
SmartPtr
(
void
)
{
if
(
m_pReference
&&
m_pReference
->
Release
(
)
==
0
)
{
SAFE_DELETE
(
m_pData
)
;
SAFE_DELETE
(
m_pReference
)
;
}
}
inline
T
&
operator
*
(
)
{
return
*
m_pData
;
}
inline
T
*
operator
->
(
)
{
return
m_pData
;
}
SmartPtr
<
T
>
&
operator
=
(
const
SmartPtr
<
T
>
&
sp
)
{
if
(
this
!=
&sp
)
{
if
(
m_pReference
&&
m_pReference
->
Release
(
)
==
0
)
{
SAFE_DELETE
(
m_pData
)
;
SAFE_DELETE
(
m_pReference
)
;
}
m_pData
=
sp
.
m_pData
;
m_pReference
=
sp
.
m_pReference
;
m_pReference
->
AddRef
(
)
;
}
return
*
this
;
}
SmartPtr
<
T
>
&
operator
=
(
T
*
pValue
)
{
if
(
m_pReference
&&
m_pReference
->
Release
(
)
==
0
)
{
SAFE_DELETE
(
m_pData
)
;
SAFE_DELETE
(
m_pReference
)
;
}
m_pData
=
pValue
;
m_pReference
=
new
KRefCount
;
m_pReference
->
AddRef
(
)
;
return
*
this
;
}
T
*
Get
(
)
{
T
*
ptr
=
NULL
;
ptr
=
m_pData
;
return
ptr
;
}
void
Attach
(
T
*
pObject
)
{
if
(
m_pReference
->
Release
(
)
==
0
)
{
SAFE_DELETE
(
m_pData
)
;
SAFE_DELETE
(
m_pReference
)
;
}
m_pData
=
pObject
;
m_pReference
=
new
KRefCount
;
m_pReference
->
AddRef
(
)
;
}
T
*
Detach
(
)
{
T
*
ptr
=
NULL
;
if
(
m_pData
)
{
ptr
=
m_pData
;
m_pData
=
NULL
;
m_pReference
->
Reset
(
)
;
}
return
ptr
;
}
private
:
KRefCount
*
m_pReference
;
T
*
m_pData
;
}
;
class
CTest
{
public
:
CTest
(
int
b
)
:
a
(
b
)
{
}
private
:
int
a
;
}
;
int
main
(
)
{
SmartPtr
<
CTest
>
pSmartPtr1
(
new
CTest
(
10
)
)
;
SmartPtr
<
CTest
>
pSmartPtr2
(
new
CTest
(
20
)
)
;
pSmartPtr1
=
pSmartPtr2
;
}
|
智能指针使用引用计数实现时,就是最好的使用代理模式的例子。在上面的例子中,SmartPtr就是一个代理类,而T* m_pData才是实际的数据。SmartPtr代理实际的数据,去实现了指针的行为,添加了引用计数,从而实现了智能指针。
我在第一次接触代理模式的时候,看它的UML类图,发现它和适配器模式的类适配器很像,再一看,又和装饰模式非常像;不仔细区分,真的是很容易混乱的。下面就做简单的区分,说多了也都是“狡辩”了。
我在这里进行区分,你们看了,也就像我在咬文嚼字一样;这就是结构型设计模式;它们之间都有一些细微的差别。你也可以说,在适配器模式进行接口适配时,添加一些数据转换就变成了远程代理;你也可以说装饰模式虽然功能不一样,在我看来,大同小异;是的,不管你怎么说,就像1000个读者心中有1000个哈姆雷特一样,每个人对设计模式的理解都是不一样的;最重要的是我们能在实际应用中进行活学活用,如果能做到这个;不管什么设计模式,那只是一个名字,就像对于富人来说,钱只是一个银行卡上的一个数字一样。
最后,我坚信分享使我们更加进步,期待大家和我分享你的设计模式心得。