代理模式 - Proxy Patterns
Posted 有且仅有
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式 - 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 @Override public 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() @Override public 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") @Override public 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("我是很耗资源的真实服务,我被创建了!");
@Override
public String getVideo(String videoName)
return "找到视频:[" + videoName + "]并返回";
/**
* 3. 视频服务一个“代理类”。
*/
class MP4VideoProxyService implements VideoService
/** 真实服务类的引用,当真正需要时才给其赋值 */
private VideoService mp4VideoService;
@Override
public 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() @Override public 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的主要内容,如果未能解决你的问题,请参考以下文章