博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式学习04:代理模式
阅读量:6237 次
发布时间:2019-06-22

本文共 5402 字,大约阅读时间需要 18 分钟。

设计模式学习04:代理模式

本文转载:

作者:

 

什么是代理模式

   代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。
   这句话看起来比较抽象,不太好理解,举个现实的例子:外卖。在有饿了么之类的外卖出现前,我们如果点了外卖很多时候都是由店家自己来配送的。但是在有了饿了么等外卖平台后,商家就把送餐的业务委托给外卖平台,由外卖小哥来配送食品。外卖平台在这里充当一个“中介”的角色,代理模式就跟这个很相似。

代码实现

 

静态代理

   以送外卖为例子来举个例子,整个过程中的核心是配送,所以我们首先定义一个接口,接口里有一个配送方法

public interface FoodDelivered {    /**配送*/    void send();}

 

   上段代码中有个send()方法,该方法是用来实现配送功能的,按照以前的方式,配送服务需要商家来自己来配送,所以我们定义一个商家类,该商家类实现FoodDelivered接口

public class Restaurant implements FoodDelivered {    @Override    public void send() {        System.out.println("派送食物");    }}

 

   商家类实现FoodDelivered接口,并实现了send方法进行配送食品。再写一个测试类来测试一下上述代码

public static void main(String[] args) {        Restaurant r = new Restaurant();        r.send();    }

 

   控制台打印(执行结果)

派送食物

派送食物Process finished with exit code 0

 

   测试方法里创建了一个商家对象,并调用send方法来进行配送服务,可以看到控制台里打印了派送食品,这里表示商家进行了配送服务。现在我们有了外卖平台,商家就可以把派送服务委托给外卖小哥来完成,外卖小哥便成了代理者,因为我们的小哥也是进行配送服务的,所以我们再定义一个外卖小哥的类,并实现
FoodDelivered接口

public class Deliveryman implements FoodDelivered {    private Restaurant restaurant;    public Deliveryman(Restaurant restaurant) {        this.restaurant = restaurant;    }    @Override    public void send() {        System.out.println("外卖小哥派送");        this.restaurant.send();    }}

 

   这里我们注意一下,Deliveryman类里有个成员是Restaurant类型的,这个类型是商家的类型,就是我们的委托人(注:这里构造方法的参数是Restaurant类型,也就是说我们的委托人可以是Restaurant类及其子类,这样灵活性很差。这个参数可以换成FoodDelivered类型,这样只要实现了FoodDelivered接口的所有类都可以作为委托人,灵活性会提高)。同时构造方法里会传一个Restaurant对象过来,已完成对成员restaurant的初始化。然后在send方法里调用委托人的send方法,并在前边打印一句“外卖小哥派送”,然后编写测试代码

public static void main(String[] args) {        Restaurant restaurant = new Restaurant();        Deliveryman deliveryman = new Deliveryman(restaurant);        deliveryman.send();    }

 

   控制台打印(执行结果)

外卖小哥派送派送食物Process finished with exit code 0

 

   这里可以我们首先创建一个商家对象,然后创建一个外卖小哥对象,并把商家对象作为参数传递给外卖小哥对象,最后我们调用外卖小哥的send方法,而不是商家的send方法,这样就把配送的服务委托给了外卖小哥来完成。控制台打印结果可以看到这个配送是由外卖小哥来配送的。
   以上方式我们称之为静态代理,静态代理是在代码编译期前进行的,代码编译后就无法再改变了。引入代理模式好处,很多时候我们的业务可能会增加部分功能,比如事务控制,日志记录或者其他一些功能。如果在每次有需求增加的时候,我们都直接去修改当前业务的代码,这个并不符合开闭原则(对修改关闭,对扩展开放),当我们使用代理模式后,可以修改代理类的方法,原业务代码并不做任何修改。还是按之前的例子来说,比如这个时候客户需要一瓶饮料,这个时候我们不用修改商家的配送方法,只需要在外卖小哥的配送方法里实现买饮料的功能就可以了,这样就实现了在不修改配送方法的前提下对配送功能进行了扩充。对于我们在平常的开发中,比如有个业务需要增加日志功能,如果要去修改原业务代码,一是破坏开闭原则,二是增加了工作量,这个时候我们就可以用代理模式对业务做很好的扩充。与静态代理对应的还有一种是动态代理。

 

动态代理

   动态代理跟静态代理一个很重要的区别在于,动态代理是在内存是中的,是在代码编译期后在内存是实现的,而静态代理是我们自己编写代理类,编译后生成class文件。动态代理需要借助两个类:java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy。我们还是以上边的例子来实现动态代理。首先需要创建一个类,并实现java.lang.reflect.InvocationHandler接口

public class FoodProxy
implements InvocationHandler { public T obj; public FoodProxy(T t) { obj = t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始。。。"); Object result = method.invoke(obj, args); System.out.println("结束。。。"); return result; }}

 

   其中有一个泛型成员变量obj,通过构造法方法接收一个变量来对成员obj初始化。重点看invoke方法,这里用到了反射相关的知识,第二个参数是Method的类型的变量,在方法里我们通过method.invoke()来执行这个方法,并且在方法执行前后各打印一行字符串。现在我们来编写一个测试类

public class FoodProxyMain {    public static void main(String[] args) {        Restaurant restaurant = new Restaurant();        FoodProxy
proxy = new FoodProxy<>(restaurant); FoodDelivered r = (FoodDelivered) Proxy.newProxyInstance(FoodDelivered.class.getClassLoader(), new Class[]{FoodDelivered.class}, proxy); r.send(); }}

 

   首先创建一个要代理的对象(商家):restaurant,然后再创建一个InvocationHandler接口的实现类对象,也就是FoodProxy对象,因为我们要代理商家,所以把restaurant作为参数传递给FoodProxy的构造方法。接着通过Proxy.newProxyInstance()方法来创建我们的代理对象。我们可以看一下newProxyInstance的参数说明

  

 

 第一个参数是代理类的加载器,第二个参数是代理类要实现的接口列表,第三个就是要绑定InvocationHandler对象。然后把执行返回的对象强转为FoodDelivered类型的对象,并调用send()方法,看控制台打印

开始。。。派送食物结束。。。Process finished with exit code 0

 

   可以看一下,控制台打印内容是InvocationHandler的实现类FoodProxy的invoke里的内容,我们在调用r.send()的时候,并不是直接去执行被代理类(Restaurant)的send()方法,而是委托给了FoodProxy的invoke去执行,也就是invoke方法里的

Object result = method.invoke(obj, args);

 

这行代码。这样就实现了对商家(Restaurant)的动态代理。这里我们可以修改一下测试类的代码,把Proxy.newProxyInstance的第一个参数和第二个参数换成实现类试试

public class FoodProxyMain {    public static void main(String[] args) {        Restaurant restaurant = new Restaurant();        FoodProxy
proxy = new FoodProxy<>(restaurant); FoodDelivered r = (FoodDelivered) Proxy.newProxyInstance(Restaurant.class.getClassLoader(), new Class[]{Restaurant.class}, proxy); r.send(); }}

 

   注意:这里参数都改为了实现类的类型,然后我们运行一下代码

Exception in thread "main" java.lang.IllegalArgumentException: com.memory.proxy.Restaurant is not an interface    at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)    at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)    at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)    at java.lang.reflect.WeakCache.get(WeakCache.java:127)    at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)    at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)    at com.memory.proxy.FoodProxyMain.main(FoodProxyMain.java:14)

 

 

  程序出现了异常,异常信息:com.memory.proxy.Restaurant is not an interface。提示Restaurant不是一个接口。这里就说明,我们要动态代理的必须是个接口,如果你要动态实现的类没有实现任何接口,那很抱歉,你将无法进行动态代理。因为我们的Restaurant类已经实现了FoodDelivered接口,所以我们在这里参数使用FoodDelivered接口就不会有问题。

总结

   代理模式是JAVA设计模式里非常重要,也是经常使用的设计模式,代理模式可以在不改变原程序代码的前提下,对功能进行扩充。通过代理模式,我们可以很方便的给代码添加日志记录,事务控制,权限控制等一系列功能。Spring AOP(面向切面编程)就用到了动态代理模式,在往后也会对Spring AOP进行研究和分析。

 

 

 

 

转载于:https://www.cnblogs.com/wobuchifanqie/p/10834194.html

你可能感兴趣的文章
技术总结
查看>>
mysql基本操作
查看>>
redis设置最大内存上限对置换策略的解读
查看>>
IOS代码布局(一) UIView
查看>>
北大光华管理学院公开课北京站
查看>>
android 随手记 图片缓存
查看>>
HTTP请求协议中请求报文(Request Headers)跟响应报文(Response Headers)的简单理解...
查看>>
es7 class装饰器
查看>>
(转)dubbo远程调用细节
查看>>
zabbix——环境安装
查看>>
查阅API文档
查看>>
软件下载地址
查看>>
移动HTML 5前端性能优化指南
查看>>
Tcl学习之--文件操作
查看>>
检測wifi是否须要portal验证 公共场所wifi验证
查看>>
操作系统-实验二作业调度模拟程序
查看>>
系统编程第一次实验
查看>>
Win7x64安装Oracle11201x64 解决PLSQL Developer无法找到oci问题
查看>>
eclipse设置快速提示符
查看>>
单个页面横屏!
查看>>