案例分析:设计模式与代码的结构特性
1 代理模式
代理模式的定义:
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的结构
代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。
代理模式的主要角色如下。
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
代理模式的主要优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
代理模式的主要缺点:
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度;
2 代理模式的具体实现
下面使用Java语言实现一个聊天程序,实现了代理模式。在聊天程序中,需要使用Java Socket API,这时使用代理模式可以起到保护Socket对象的作用,同时降低了程序对Socket API的耦合度,可以方便地扩展进行Socket通信时的功能。
先声明一个抽象主题接口,用于声明客户端程序需要的方法。该接口只提供了客户端所需四个方法,屏蔽了Socket类的其他方法,提高了一定的安全性。
public interface AbstractSocket {
int getLocalPort();
OutputStream getOutputStream() throws IOException;
InputStream getInputStream() throws IOException;
void close() throws IOException;
}
然后写一个真实主题类,实现抽象主题接口中的方法。该类对象是真正与Socket对象发生关系的对象,是我们最终要引用的对象。
public class IntermediarySocket implements AbstractSocket {
private Socket socket;
public IntermediarySocket(String host, int port) throws IOException {
this.socket = new Socket(host, port);
}
public IntermediarySocket(Socket serverSocket) {
this.socket = serverSocket;
}
@Override
public int getLocalPort() {
return this.socket.getLocalPort();
}
@Override
public OutputStream getOutputStream() throws IOException {
return this.socket.getOutputStream();
}
@Override
public InputStream getInputStream() throws IOException {
return this.socket.getInputStream();
}
@Override
public void close() throws IOException {
this.socket.close();
}
}
接着,给出代理类,代理类实现了和真实主题相同的接口,即抽象主题接口。客户端代码通过直接调用代理类来来进行Socket通信。
public class ProxySocket implements AbstractSocket {
private IntermediarySocket socket;
public ProxySocket(String host, int port) throws IOException {
this.socket = new IntermediarySocket(host, port);
}
public ProxySocket(Socket serverSocket) {
this.socket = new IntermediarySocket(serverSocket);
}
@Override
public int getLocalPort() {
return this.socket.getLocalPort();
}
@Override
public OutputStream getOutputStream() throws IOException {
return this.socket.getOutputStream();
}
@Override
public InputStream getInputStream() throws IOException {
return this.socket.getInputStream();
}
@Override
public void close() throws IOException {
this.socket.close();
}
}
真实主题类和代理主题类均实现了抽象主题接口,在客户端类的构造函数中使用了基于接口的多态机制。客户端类提供了两个构造函数,第一种传入了AbstractSocket接口类型的参数,第二种构造函数生产了一个实现 AbstractSocket 接口的 ProxySocket 类型参数,主函数中实际调用的是第二种构造函数,即利用了Java的多态机制。
public Client(String name, AbstractSocket socket) throws IOException {
/*
具体实现
*/
}
public Client(String name, String host, int port) throws IOException {
this(name, new ProxySocket(host, port));
}
public static void main(String[] args) throws IOException {
new Client("Client", "127.0.0.1", 2001);
}
该程序的所有代码详见Github。