本文主要是介绍代理模式 - Proxy Patterns,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本篇的父博文是:设计模式 - Design Patterns
一、代理模式的意义
Design Patterns 书中对代理模式的描述是这样的:
Provide a surrogate or placeholder for another object to control access to it.
代理模式为一个对象提供一个代理对象,以控制对这个对象的访问。
代理模式很简单有趣,当然也很重要。往下看就很快明白啦~
二、应用场景
所以,为什么要用代理模式呢?或者说代理模式适用于哪些场景呢?
1. 远程代理人(remote proxy)
试想这样一个场景:你需要引用一个网络中的远程对象(对象在其它计算机JVM中)来完成功能的调用,你该怎么引用?
HelloService helloService = new 这该写啥啊?();
稍微上一点规模的系统都会有多个应用的互相调用的需求。那么,在Java 中这代码我们该如何写呢?
没错,最优的代码模板就是用代理模式 的思想去实现。
思路:
在本地项目中创建一个远程对象的代理对象 - 作为远程对象的代表,并在代理对象中封装对远程程序的访问/调用细节。此时,代码中不再直接访问远程对象,而是访问这个代理对象。如此,你就如同访问一个本地对象那样访问了远程对象。
下面用一个简单写的订单服务 - OrderService
作为例子来实现下。
实现步骤:
首先是一个接口:订单服务,提供了一个方法创建订单。
/*** 订单服务。*/ public interface OrderService {/*** 创建订单。* @param orderId 订单ID* @return*/String createOrder(Integer orderId) throws Exception;}
其次呢,其它程序员在远程主机(127.0.0.1)上实现了此接口,并启动了对外服务
public class OrderRemoteService implements OrderService {@Overridepublic String createOrder(Integer orderId) throws Exception {return "我是127.0.0.1,你创建订单成功!ID:" + orderId;}}
启动对外服务
/*** 启动对外远程服务的main方法类。*/ public class RomteMain {@SuppressWarnings({ "resource" })public static void main(String[] args) throws Exception {// 实际实现类 - 敲黑板!这里是上面那个实际实现类OrderService orderSevice = new OrderRemoteService();// 下面代码:ServerSocket 监听 client 的Socket 来连接,连上了就根据约定读取client 调用的什么方法,然后转而调用实际实现类,并返回结果ServerSocket server = new ServerSocket(9999);// 监听端口while (true) {final Socket socket = server.accept();new Thread(){@Overridepublic void run() {try {ObjectInputStream input = new ObjectInputStream(socket.getInputStream());// 读方法名String methodName = input.readUTF();// 读参数类型Class<?> paramsType = (Class<?>)input.readObject();// 读参数值Object params = (Object)input.readObject();// 使用反射调用相关方法Method method = orderSevice.getClass().getMethod(methodName, paramsType);Object result = method.invoke(orderSevice, params);// 将结果写回给socket ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());output.writeObject(result);} catch (Exception e) {e.printStackTrace();}}}.start();}} }
然后呢,在我们自己程序里写一个代理类,代理类就作为上面那个实现类在我们本地一个代表
/*** 我是远程对象的代理人。* <p>代理对象去找幕后主使来完成任务</p>*/ public class OrderServiceProxy implements OrderService {@SuppressWarnings("resource")@Overridepublic String createOrder(Integer orderId) throws Exception {// 1. 连接远程服务Socket socket = new Socket("127.0.0.1", 9999);ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());// 自定一个写出顺序,远程对象那里也要按这个顺序读output.writeUTF("createOrder");// 写出方法名output.writeObject(Integer.class);// 写出参数类型output.writeObject(orderId);// 写出参数值// 2. 读取返回ObjectInputStream input = new ObjectInputStream(socket.getInputStream());return (String)input.readObject();} }
最后呢,我们程序中要用那个远程对象,我们没法直接引用到它,没关系我们用它的代理人
public class LocalMain {public static void main(String[] args) throws Exception {// 客户端引用代理对象,而不是实际对象OrderService orderService = new OrderServiceProxy();// 直接调用服务就好System.out.println(orderService.createOrder(9527));} }
以上。
总结:
我们已经简单写了一个代理模式 - 远程代理 的Java 程序。
顺便说下,我们写这个也是个简单的RPC 框架~~
不过有一定经验的读者会发现,上面代码还是比较挫的,这在Java 中叫所谓的“静态代理”。如果我们要多写几个代理类,多写几个方法,上面代码扩展性就极差了。
但是,上面只是简单尝试实现了“远程代理”,如果要做一个稍微完善点的RPC ,我们当然需要用到Java 中的“动态代理”。实际上,Java 中之所以有“动态代理”,就是为了解决上面说的那类问题的。不过这里暂且不表了,这个并非主线剧情。
还有一点,是不是还会有人脑海中会有这样一种思考?这不就是个接口的实现类吗,怎么能叫代理模式呢?
首先,再看一遍设计模式的概念。
接下来说:你写一个实现类,是你依赖Java 提供的一种实现
Interface
的代码形式而写出的代码;而你写一个代理类是参照设计模式的方案和思路写出的代码;它们是两个领域两种完全不同的概念啦。
2. 虚拟代理(virtual proxy)
思路:
如果一个对象的创建成本非常高且并不是随时需要它,那么在程序启动时我们就不需要创建此对象,而是仅仅创建此对象的一个代理,让客户端引用这个代理就好了。当客户端实际需要使用这个对象的时候,我们再创建这个对象。
实现步骤:
/*** 4. 某个客户程序。*/
public class ClientMain {/** 只给客户端持有一个视频服务的代理类,而不是真实服务类 */private static VideoService videoService = new MP4VideoProxyService();public static void main(String[] args) throws Exception {// 客户端程序启动了System.out.println("程序启动了--------");System.out.println("程序运行很久很久--------");Thread.sleep(1000l);// 很久后才突然被触发,需要执行以下代码System.out.println("N小时候触发了某个事件,我们需要获取视频了--------");String result = videoService.getVideo("长城");System.out.println(result);}
}/*** 1. 视频服务“接口”。*/
interface VideoService {String getVideo(String videoName);
}
/*** 2. 视频服务“真实服务类”。* <p>初始化很消耗资源,很耗时</p>*/
class MP4VideoService implements VideoService {public MP4VideoService() {// 很耗时、耗资源的初始化操作System.out.println("我是很耗资源的真实服务,我被创建了!");}@Overridepublic String getVideo(String videoName) {return "找到视频:[" + videoName + "]并返回";}
}/*** 3. 视频服务一个“代理类”。*/
class MP4VideoProxyService implements VideoService {/** 真实服务类的引用,当真正需要时才给其赋值 */private VideoService mp4VideoService;@Overridepublic String getVideo(String videoName) {// 代理类的getVideo方法if (mp4VideoService == null) {mp4VideoService = new MP4VideoService();}return mp4VideoService.getVideo(videoName);}
}
3. 保护代理(protection proxy)
思路:
使用一个代理对象来控制一个实际对象的访问权限。这种用处就先不写代码了,很好想到,就是保护一个对象,使不同的客户端有不同的访问权限。
4. 智能引用(smart reference)
思路:
智能引用取代了简单的指针引用,在对象被访问时能让我们做一些额外的操作。
实现步骤:
这个最大名鼎鼎的应用就是AOP 了吧。
正好,接下来我使用上面没有使用的,Java动态代理 的方式来实现一个AOP 代码。
首先,我们创建一个
interface
叫CalculateService
,用来表示计算服务,并定义加、减、乘和除四个方法public interface CalculateService {int calculateAdd(int a, int b);int calculateSubtract(int a, int b);int calculateMultiply(int a, int b);int calculateDivide(int a, int b); }
然后,我们用一个类来实现这个接口的方法,这个类就是作为一个真是实现类
public class CalculateServiceImpl implements CalculateService {public int calculateAdd(int a, int b) {return a + b;}public int calculateSubtract(int a, int b) {return a - b;}public int calculateMultiply(int a, int b) {return a * b;}public int calculateDivide(int a, int b) {return a / b;}}
最后,我们用动态代理的
API
,来实现AOP ,我们统计下耗费的时间。public class MainAOP {// 现在我们有一个服务CalculateService,它提供了基本的加减乘除。// * 突然的,你多了个需求,想在执行加减乘除方法时,都计算一下它执行所耗费的时间。// ** 咋办?去改CalculateServiceImpl吗?有时候你没有权限改这个/你不应该侵入人家的代码/其它调用这个服务的人可能并不需要计时// ** 那代码怎么写能很好的解决这个问题呢?// *** 灯~灯~灯~灯…… 所以你学了设计模式,你想到“可以用aop的思想 + JavaSE中提供的Proxy等类的功能”来写代码。开始!/** 计算服务 */private static CalculateService calculateService = getCalculateService();/************************************ 模拟使用↓ **************************************/public static void main(String[] args) {// 牛!calculateService.calculateAdd(1, 1);}/*** 获得计算服务。<br>* 你利用你的aop思想,将代码写成了下面这样。<br>* 你不费吹灰之力,没有写代理类.java的情况下,把功能给完成了。还对原代码无侵入,还能随意判断,以根据不同的方法去决定是否要计算时间。牛!* @return*/private static CalculateService getCalculateService() {return (CalculateService) Proxy.newProxyInstance(MainAOP.class.getClassLoader(), new Class[] { CalculateService.class }, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();// 核心调用实现类Object result = method.invoke(new CalculateServiceImpl(), args);long endTime = System.currentTimeMillis();// 此次aop的目的:输出耗时System.out.println("耗时:" + (endTime - startTime));return result;}});} }
需要解释的点,代码中的注释都写上啦。
以上我们了解了:设计模式之 > 结构模式之 > 代理模式。
请持续关注我的博客哈,会继续推出设计模式系列。
转载注明出处:http://blog.csdn.net/u010297957/article/details/53885454
这篇关于代理模式 - Proxy Patterns的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!