1.代理模式
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。这样做的好处是降低了耦合性,而且具有方法增强、高扩展性的设计优势。

代理模式其实就是提供一个新对象,实现对真实对象的操作或成为真实对象的替身。代理模式涉及两个概念,一个是目标类,一个是代理类。
目标类:原对象,需要通过代理对象控制它的访问,扩展其功能。
代理类:代理模式产生的的新对象,是原对象的替身,已经在原有基础上修改了逻辑。
比如你要去饭馆吃饭,你不用自己去饭馆,只需要点外卖,让外卖小哥去饭馆拿到饭,给你送到家里就可以,这就是一个典型的日常生活中代理模式的应用。这里你自己就是目标类,而外卖小哥就是代理类。

代理模式应用场景:
①当一个对象不能或不想直接访问另一个对象时,可以通过一个代理对象来间接访问。
②被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容。

代理模式能实现控制访问某个对象的能力,在某些情况下,一个对象的某些方法想要进行屏蔽或者某种逻辑的控制,就可以通过代理的方式进行。在此能力上引申出来的作用,也是目前在开发中经常使用的一个作用:在不修改原对象代码的基础上,对原对象的功能进行修改或者增强。

代理模式符合java开发的开放原则,避免大幅度修改原先的代码,只是在原有的基础上进行扩展。代理模式的优势概括起来有两点:
①解耦
通过引入代理对象的方式来间接访问目标对象,当需要给某个对象添加一些额外的逻辑时,使用代理模式可以不修改原代码,只针对这些额外的功能进行编码。在整个过程中,原对象的逻辑和额外增加逻辑完全解耦,互不干扰。
②高扩展性
通过代理对象可以对原有的业务进行扩展。由于模块间都是解耦的,所以可以随时添加任意的功能或修改原来的功能而不会影响到原模块的正常执行。

代理模式中的角色:
①Client 客户类,即使用代理类的类。
②Subject 抽象主题类,抽象了代理行为主要职责,声明目标对象与代理对象的共同接口,该类既可以是一个抽象类,也可以是一个接口。
③RealSubject 实现抽象主题的真实主题类,即目标类或被委托类、被代理类,它定义了代理所表示的真实对象,执行具体的业务逻辑方法,而客户端则通过代理类间接地调用真实主题类中定义的方法。
④Proxy 委托类或代理类,持有对真实主题类的引用,可以访问真实实体,提供一个与Subject接口相同的接口,这样代理就可以用来替代实体,控制对实体的存取,并可能负责创建或删除它。

代理模式分为静态代理和动态代理。静态代理应用的不多,因为违反了开闭原则,动态代理才是主流。

2.静态代理
代理对象和被代理对象在代理之前都是确定的,它们都实现相同的接口或者继承相同的抽象类。
静态代理需要创建一个和真实对象相同的类,将真实对象当作参数传入到代理中,当外界通过代理进行操作的时候,代理中的真实对象也会进行相应的操作。

①使用继承的方式:
顾客(目标类)Customer.java:
public class Customer {
public String order(String foodName) {
return “已经下单了”+foodName;
}
}
外卖小哥(代理类)DeliveryClerk.java:
public class DeliveryClerk extends Customer{
@override
public String order(String foodName) {
String result = super.order(foodName);
Log.i(TAG,“已经接收订单,正前往取餐途中”);
Log.i(TAG,“已经取餐,正在派送”);
return result+“已经搅拌均匀”;
}
}
测试类:
//创建一个目标类
Customer customer = new DeliveryClerk(); //已经给顾客找了一个替身,即DeliveryClerk
String result = customer.order(“麻婆豆腐”);
Log.i(TAG,result);

打印结果为:
已经接收订单,正前往取餐途中
已经取餐,正在派送
已经下单了麻婆豆腐已经搅拌均匀

从打印结果可以看到,调用order方法后,实际执行的是代理类的方法,对目标类进行了增强。

②使用接口的方式:
1)创建接口,定义共同的方法;
2)创建目标类,实现接口;
3)创建代理类,实现接口,但是增加一步,在构造方法中传入目标对象的引用,拿到目标对象。在目标对象的方法调用之前和之后可以进行扩展;
4)得到代理对象,通过对代理对象操作,实现对目标对象的相应操作。

//1.定义接口
public interface OrderInterface {
public String order(String foodName);
public String test();
}
//2.创建目标类,实现OrderInterface接口
public class Customer implements OrderInterface{
@Override
public String order(String foodName) {
return “已经下单了”+foodName;
}
@Override
public void test() {
}
}
//3.创建代理类,同样实现OrderInterface接口,并持有目标类的引用
public class DeliveryClerk implements OrderInterface{
//把目标对象传入,并存到成员变量
private OrderInterface customer;
public DeliveryClerk(OrderInterface customer){
this.customer = customer;
}
@override
public String order() {
String result = customer.order(“麻婆豆腐”);
Log.i(TAG,“已经接收订单,正前往取餐途中”);
Log.i(TAG,“已经取餐,正在派送”);
return result+“已经搅拌均匀”;
}
@override
public void test() {
}
}
在代理类的构造方法里创建了一个Customer目标对象,在调用代理类的order()方法时,实际是调用的目标对象Customer的order()方法,但是通常会附加一些逻辑,比如打印一些log,直接在代理类的方法里添加就可以。这样做的好处就是目标实现类的逻辑不用做任何改变。
这样,对于客户来说,完全不用跟OrderInterface进行直接交互。
Customer customer = new Customer();//创建目标类对象
OrderInterface deliveryClerk = new DeliveryClerk( customer); //创建代理类对象
String result = deliveryClerk.order(“麻婆豆腐”); //调用代理对象的方法
Log.i(TAG, result);

打印结果为:
已经接收订单,正前往取餐途中
已经取餐,正在派送
已经下单了麻婆豆腐已经搅拌均匀

同样,调用order方法后,实际执行的是代理类的方法,对目标类进行了增强。

静态代理虽然能够实现代理模式,完成了解耦,但是静态代理类的代码维护依然非常复杂,一旦接口或者父类发生了变动,代理类的代码就得随之改变。而且,静态代理都是自己先写好的代理类,这样的代理关系都是固定的,当代理多个真实对象的时候就要写多个代理类,并且会产生冗余的代码,扩展性和可维护性都不高,也就是静态代理违反了设计模式的开闭原则。这时候就要用到动态代理了。

3.动态代理
动态代理是在内存中生成代理对象的一种技术,也就是整个代理过程在内存中进行,不需要手写代理类的代码,也不会存在代理类编译的过程,而是直接在运行期,在虚拟机内“凭空”造出一个代理类对象。

①为什么类可以动态生成?
Java虚拟机的类加载过程主要分为五个阶段:加载、验证、准备、解析、初始化。其中加载阶段需要完成3件事情:
1)通过一个类的全限定名来获取定义此类的二进制字节流;
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
3)在内存中生成一个代表这个类的 java.lang.Class对象,作为方法区中这个类的各种数据访问入口;
关于第1点获取类的二进制字节流(class字节码)就有很多途径,比如:
1)从ZIP包获取,这是JAR、EAR、WAR等格式的基础;
2)从网络中获取,典型的应用是 Applet;
3)运行时计算生成,这种场景使用最多的是动态代理技术,在java.lang.reflect.Proxy类中,就是使用ProxyGenerator.generateProxyClass为特定接口生成形式为*$Proxy的代理类的二进制字节流;
4)由其它文件生成,典型应用是JSP,即由JSP文件生成对应的Class类从数据库中获取等;
所以,动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。

②明白了类的动态生成,然后看一下动态代理的原理
动态代理是基于反射实现了在程序运行的过程中才决定代理什么对象。

动态代理有两种实现方式:基于JDK(接口)的动态代理和基于CGLIB(父类)的动态代理。

(1)基于JDK(接口)的动态代理
这是JDK自带的动态代理技术,这种实现方式有一个缺点,就是它要求被代理对象,也就是目标类,必须实现接口。生成的代理对象和原对象都实现相同的接口,它们是兄弟关系。

java提供了动态代理接口InvocationHandler和动态代理类Proxy供开发者使用,它们都在java.lang.reflect包中,可见动态代理和反射有密不可分的关系。

InvocationHandler接口定义:
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
这个方法表示代理对象proxy要调用目标对象的method。参数proxy表示代理对象,method表示目标对象被调用的方法;args表示被调用的方法的参数。
InvocationHandler接口的作用就是在invoke方法中执行目标对象的方法。需要为目标对象定义一个实现了这个接口中的invoke方法的动态代理类,同时在创建这个动态代理类的实例时,还要在方法或构造中传入真实对象的引用,即InvocationHandler的实现类需要持有真实对象的引用,这样才能执行真实对象的方法。

Proxy类定义如下:
public class Proxy implements Serializable {
protected InvocationHandler h;//持有一个InvocationHandler类型的引用
protected Proxy(InvocationHandler h) {
this.h = h;
}
//根据指定的类加载器和接口来获取代理对象的Class对象
public static Class<?> getProxyClass( ClassLoader loader, Class… interfaces) throws IllegalArgumentException {
//…
}
//根据指定的类加载器和接口生成代理对象
public static Object newProxyInstance( ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
//…
}
//…
}
Proxy类的作用是用来动态的创建一个代理对象,它内部会持有一个InvocationHandler引用(在构造方法中传入),它提供了许多的方法,但是常用的是getProxyClass方法和newProxyInstance方法:
getProxyClass(重点方法):作用是在运行时根据.class结构生成一个代理Class二进制流,并通过传入的ClassLoader把代理Class二进制流加载成一个代理Class对象,该代理Class对象继承Proxy并实现了传入的第二个参数对应的Interface列表。
newProxyInstance(常使用的方法): 作用是在运行时根据代理Class对象生成代理对象实例,这个方法中会先调用getProxyClass方法生成代理Class对象,在获取到代理Class对象后,根据第三个参数InvocationHandler引用通过反射创建代理对象实例,所以newProxyInstance最终的结果是生成一个代理对象实例,该代理对象会继承Proxy类并实现给定的接口列表,同时内部持有一个InvocationHandler引用。
可见,动态代理通过JDK的Proxy对象和反射机制支撑起了动态代理的核心功能。

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数代表的意义:
ClassLoader:固定写法,指定目标类对象的类加载器,用于加载目标类及其接口的字节码文件。使用目标类的字节码对象调用getClassLoader()方法即可得到。
Class<?>[] interfaces:固定写法,指定目标类实现的所有接口的字节码对象的数组。使用目标类的字节码对象调用getInterfaces()方法即可得到。
InvocationHandler:这个参数是一个接口,可以理解成拓展的需求服务。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable ;
}
InvocationHandler是一个接口,只有一个invoke抽象方法,它会在代理类对象调用方法时执行。也就是说,在代理类对象中调用任何接口中的方法时,都会执行到invoke方法中,所以,可以在该方法中完成对增强或者扩展代码逻辑的编写。
invoke(Object proxy, Method method, Object[] args),四个参数的意义:
proxy:代理类对象的一个引用,也就是Proxy.newProxyInstance方法的返回值。
method:被代理的方法,对应的是触发invoke时执行的方法的Method对象。比如调用xxx方法,该方法触发invoke方法的执行,那么method就是xxx方法对应的反射对象(Method对象)。
args:代理对象调用方法时,传递的实际参数。

现在ClassLoader、方法名和方法参数都有了,就可以使用反射调用了。

使用动态代理的基本步骤如下:
1、定义代理对象和真实对象的公共接口;(与静态代理步骤相同)
2、真实对象实现公共接口中的方法;(与静态代理步骤相同)
3、定义一个实现了InvocationHandler接口的动态代理类;
4、通过Proxy类的newProxyInstance方法创建代理对象,调用代理对象的方法。

①定义接口
public interface OrderInterface {
public String order(String foodName);
public String test();
}
②接口的实现类
public class Customer implements OrderInterface {
@Override
public String order(String foodName) {
return “已经下单了”+foodName;
}
@Override
public void test() {
}
}
③使用JDK的API动态生成一个代理对象,返回值即为代理类
OrderInterface deliveryClerk = Proxy.newProxyInstance( customer.getClass().getClassLoader(), customer.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
if(“order”.equals(method.getName())) { // 需要在order方法里增加扩展逻辑
//调用原来的method方法逻辑
Object result = method.invoke(customer, args);
//添加扩展逻辑
System.out.println(“我是扩展逻辑”);
return result;
} else { // 不需要增加扩展逻辑的方法
return method.invoke(customer, args); //使用method反射调用,在原对象(目标对象)中执行该方法,不修改其逻辑,也就是说原封不动的调用原来的逻辑
}
}
});
注意:Proxy.newProxyInstance方法的返回值是代理类,代理类和目标类是兄弟关系,但是这里不能用Customer接收,而需要用父类来接收。
④调用代理类对象,执行对应方法
String result = deliveryClerk.order(“麻婆豆腐”);
System.out.println(result);

动态代理在内存中生成代理类,其原理就类似在内存中新建了一个DeliveryClerk类,而Proxy.newProxyInstance方法中invoke做的事情就是DeliveryClerk类里每个方法做的事情。

(2)基于CGLIB(父类)的动态代理

4.静态代理和动态代理比较
静态代理缺点:
①扩展性差,如果接口新增一个方法,除了所有实现类(真实主题类)需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。
②代理对象只服务于一种类型的对象,如果要服务多类型的对象,必须要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

动态代理的优点:
①可以通过一个代理类完成全部的代理功能。接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。当接口方法数量较多时,可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
②动态代理的应用使类的职责更加单一,复用性更强。
动态代理的缺点:
①JDK方式不能对类进行代理,只能对接口进行代理,如果类没有实现任何接口,那么就不能使用这种方式进行动态代理。

5.动态代理应用
Retrofit中使用了动态代理,简要分析一下。

以Retrofit入口代码开始:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(……)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(……)
.addCallAdapterFactory( RxJava3CallAdapterFactory.create())
.client(client)
.build();
apiServer = retrofit.create(ApiServer.class);

具体看一下Retrofit的create方法,就是在这里使用了动态代理:
public T create(final Class service) {
validateServiceInterface(service);//验证传入的service是否是一个接口,并且不能是泛型
//动态代理的方法,返回定义的service的实例
return (T) Proxy.newProxyInstance( service.getClassLoader(), new Class<?>[] {service}, new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
//代理类的生成默认会继承Object,调用代理类的方法
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return loadServiceMethod( method).invoke(args);
}
});
}

Android设计模式—代理模式★相关推荐

  1. Android常见设计模式——代理模式(Proxy Pattern)(二)

    文章目录 1. 前言 2. 远程代理(Remote Proxy) 3. 后记 1. 前言 在上篇Android常见设计模式--代理模式(Proxy Pattern)中基本上知道了什么是代理模式,以及对 ...

  2. Python设计模式-代理模式

    Python设计模式-代理模式 基于Python3.5.2,代码如下 #coding:utf-8info_struct = dict() info_struct["addr"] = ...

  3. Java设计模式(代理模式-模板方法模式-命令模式)

    Java设计模式Ⅴ 1.代理模式 1.1 代理模式概述 1.2 静态代理 1.2.1 静态代理概述 1.2.2 代码理解 1.3 动态代理之JDK代理 1.3.1 动态代理之JDK代理概述 1.3.2 ...

  4. 设计模式——代理模式

    设计模式--代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能 ...

  5. 23种设计模式----------代理模式(一)

    代理模式也叫委托模式. 代理模式定义:对其他对象提供一种代理从而控制对这个对象的访问.就是,代理类 代理 被代理类,来执行被代理类里的方法. 一般情况下,代理模式化有三个角色. 1,抽象的主题类(或者 ...

  6. java设计模式代理模式_Java中的代理设计模式

    java设计模式代理模式 代理对象或代理对象为另一个对象提供占位符,以控制对该对象的访问. 代理充当原始对象的轻量级版本或简化版本. 它支持与原始对象相同的操作,但可以将那些请求委托给原始对象以实现它 ...

  7. 第四章 Caché 设计模式 代理模式

    文章目录 第四章 Caché 设计模式 代理模式 定义 类型 使用场景 优点 缺点 结构图 完整示例 抽象主题类 真实主题类 代理类 对象类 调用 思考 第四章 Caché 设计模式 代理模式 定义 ...

  8. sheng的学习笔记-设计模式-代理模式

    原理图: 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. 至少在以下集中情况下可以用 ...

  9. 设计模式-代理模式(Proxy Pattern)

    设计模式-代理模式(Proxy Pattern) 文章目录 设计模式-代理模式(Proxy Pattern) 一.定义 二.概念解释 三.场景 四.实现 1.类图 2.代码实现 五.小结 六.动态代理 ...

最新文章

  1. python笔记基础-python基础学习笔记(一)
  2. 第四章-分布式数据库HBase
  3. 基本服务-使用大使网关
  4. c++--运算符重载
  5. 辅助排序和Mapreduce整体流程
  6. html简单弹窗代码_真的!!!两行css代码实现瀑布流,html,css最简单的瀑布流实现方式且没有缺点!...
  7. yolov2训练_YOLOv2 : YOLO9000:Better,Faster,Stronger解读
  8. 房费制——报表(1)
  9. 用C#,SQL Server编写的音乐播放软件
  10. Github使用教程Git下载文件
  11. 小米路由器mini刷老毛子教程
  12. 协同过滤推荐算法总结
  13. 如何恢复按下Shift + DELETE键永久删除的文件和资料夹?
  14. 案例教程:一步步教你ps制作二寸照片
  15. 原生js实现可切换式导航栏
  16. 如何用ChatGPT做新品上市推广方案策划?
  17. Python全局变量的隐藏“窍门”
  18. 火狐浏览器怎么清理缓存、cookie等?
  19. species-in-pieces网站动效的JS实现
  20. 【英语】VOA60-second science听力

热门文章

  1. argmin求和函数算法
  2. idc销售管理系统php版,ESM企业销售管理系统 v3.0
  3. tableau入门简介和常用操作
  4. MongoDB学习笔记(三)-----集群架构
  5. 什么是平台化?平台化是如何发展的?
  6. L1-002 打印沙漏 (20 分)(模拟,规律,二分)
  7. NAND Flash ECC算法长度计算
  8. element自定义el-dialog标题样式,实现在title部分放置按钮
  9. Js 箭头函数 详细介绍(多种使用场景差异,你学会了吗?)
  10. Laravel引入谷歌验证器