设计模式之代理模式

概念及作用

  • 为另一个对象提供代理或占位符以控制对它的访问。
  • 使用额外的间接级别来支持分布式,受控或智能访问。
  • 添加包装器和委托以保护实际组件免受过度复杂的影响。
1
2
3
4
5
6
7
8
9
10
11
设计代理或代理对象:在客户端第一次发出代理请求时实例化真实对象,记住该真实对象的身份,并将发起请求转发给该真实对象。然后,所有后续请求都直接转发到封装的真实对象。

代理模式有四种常见情况适用。

虚拟代理是“创建昂贵”对象的占位符。仅在客户端首次请求/访问对象时才创建真实对象。
远程代理为驻留在不同地址空间中的对象提供本地代表。这就是RPC和CORBA中提供的“存根”代码。
保护代理控制对敏感主对象的访问。 “代理”对象在转发请求之前检查调用者是否具有所需的访问权限。
当访问对象时,智能代理会插入其他操作。典型用途包括:
计算对真实对象的引用数,以便在没有更多引用(也称为智能指针)时自动释放它,
在第一次引用时将持久对象加载到内存中,
在访问真实对象之前检查它是否已锁定,以确保没有其他对象可以更改它。

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
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
// 5. To support plug-compatibility between
// the wrapper and the target, create an interface
interface SocketInterface {
String readLine();
void writeLine(String str);
void dispose();
}

class SocketProxy implements SocketInterface {
// 1. Create a "wrapper" for a remote,
// or expensive, or sensitive target
private Socket socket;
private BufferedReader in;
private PrintWriter out;

public SocketProxy(String host, int port, boolean wait) {
try {
if (wait) {
// 2. Encapsulate the complexity/overhead of the target in the wrapper
ServerSocket server = new ServerSocket(port);
socket = server.accept();
} else {
socket = new Socket(host, port);
}
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
} catch(IOException e) {
e.printStackTrace();
}
}

public String readLine() {
String str = null;
try {
str = in.readLine();
} catch( IOException e ) {
e.printStackTrace();
}
return str;
}

public void writeLine(String str) {
// 4. The wrapper delegates to the target
out.println(str);
}

public void dispose() {
try {
socket.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}

public class ProxyDemo {
public static void main( String[] args ) {
// 3. The client deals with the wrapper
SocketInterface socket = new SocketProxy( "127.0.0.1", 8080, args[0].equals("first") ? true : false );
String str;
boolean skip = true;
while (true) {
if (args[0].equals("second") && skip) {
skip = !skip;
} else {
str = socket.readLine();
System.out.println("Receive - " + str);
if (str.equals(null)) {
break;
}
}
System.out.print( "Send ---- " );
str = new Scanner(System.in).nextLine();
socket.writeLine( str );
if (str.equals("quit")) {
break;
}
}
socket.dispose();
}
}

(二)

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
public interface Image {
void display();
}

public class RealImage implements Image {

private String fileName;

public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}

@Override
public void display() {
System.out.println("Displaying " + fileName);
}

private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}

public class ProxyImage implements Image{

private RealImage realImage;
private String fileName;

public ProxyImage(String fileName){
this.fileName = fileName;
}

@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}

public class ProxyPatternDemo {

public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");

// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}

(三)

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
"""
Provide a surrogate or placeholder for another object to control access
to it or add other responsibilities.
"""

import abc


class Subject(metaclass=abc.ABCMeta):
"""
Define the common interface for RealSubject and Proxy so that a
Proxy can be used anywhere a RealSubject is expected.
"""

@abc.abstractmethod
def request(self):
print('请求结果')


class Proxy(Subject):
"""
Maintain a reference that lets the proxy access the real subject.
Provide an interface identical to Subject's.
"""

def __init__(self, real_subject):
self._real_subject = real_subject

def request(self):
# ...
self._real_subject.request()
class RealSubject(Subject):
"""
Define the real object that the proxy represents.
"""

def request(self):
pass


def main():
real_subject = RealSubject()
proxy = Proxy(real_subject)
proxy.request()


if __name__ == "__main__":
main()