在HeadFirst设计模式中代理模式用了比较多的篇幅来讲解,其中的例子我感觉有些繁琐,所以我们这篇就不按照惯例用例子来阐述代理模式了。我们直接进入正题,分析模式本身的设计和解决的问题。
远程代理模式
假如我们有一个系统,能够调用本地对象,然后将每个请求转发到远程对象上进行调用应该如何设计。
在客户端我们使用客户辅助对象进行调用,客户辅助对象进行远端调用,对于客户对象来说就像是在调用本地的方法一样。
在服务端,服务辅助对象从客户辅助对象中接受请求(socket连接),将调用的信息解包,然后调用真正服务对象上的方法。
我们利用代码更清楚的看到实现过程和方式,书中利用java的RIM来进行远程方法调用,我们不必纠结RIM,只要知道RIM是帮我们实现演出调用处理网络和I/O代码。
1、远端接口
首先我们需要一个接口用于客户辅助对象和服务辅助对象的统一接口。
代码语言:javascript复制public interface MyRemote extends Remote{
public String SayHello() throws RemoteException;
}
Remote 是RIM包中的接口,使用RIM需要实现Remote接口。
2、远端实现
服务实现远端接口,也就是客户端要调用的方法的接口。
代码语言:javascript复制public class MyRemoteImpl implements MyRemote{
public String SayHello(){
return "server say hello";
}
}
3、注册服务
现在我们已经实现了一个远程服务了,要他能被客户端远程调用。就需要将服务实例化并注册到RIM registry中,注册使用了rmi 中的Naming类的静态方法rebind()
我们可以直接在远程服务的main() 方法中注册就行了。
代码语言:javascript复制public static void main(String args[]){
try{
MyRemote service=new MyRemoteImpl();
Naming.rebind("RemoteHello",service);
}catch(Exception ex){
ex.printStackTrace();
}
}
4、客户端实现
由于第三步我们已有了注册服务的实现,客户端要想调用远端服务就需要通过网络发现服务并调用。利用Naming.lookup()方法返回值并将他转成远端接口进行调用。
代码语言:javascript复制public class MyRemoteClient(){
public static void main(String[] args){
new MyRemoteClient().go();
}
public void go(){
try{
MyRemote service=(MyRemote) Naming.lookup(rmi://127.0.0.1/RemoteHello);
String result=service.SayHello();
System.out.println(result);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
整个执行过程:RIM启动rmiregistry终端,启动远端服务运行到main()方法进行服务注册。客户端运行main()方法查找服务返回Object进行转换到远端接口对象,调用接口对象的方法进行代理访问远端服务。
在上面的代码中部分代码不完善只是讲解远程带来和过程,同样的.Net 实现远程代理的一个经典用例就是WCF,看看WCF的模式是不是完美契合远程代理模式。
代理模式
通过远程代理模式我们已经知道代理模式的概念和一种实现了,远程代理是一般代理模式的一种实现。因为代理模式包含许多变体,包括一般代理模式、虚拟代理模式、动态代理、缓存代理、同步代理等等。
这个类图是一般代理模式的类图。
首先Subject,它为RealSubject和Proxy提供了接口。通过实现同一接口,Proxy在RealSubject出现的地方取代它。
RealSubject是真正做事情的对象,它是被Proxy代理和控制访问的对象。
Proxy持有RealSubject的引用。在某些时候,Proxy还会负责RealSubjext对象的创建与销毁。
代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程对象、创建开销大的对象或者需要安全控制的对象。