设计模式
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。(-----摘自百度百科)
人们所熟知的设计模式共有23种,这次要研究的是其中的一种在日常开发中很常见的模式之一,代理模式。。
我们为什么要用代理模式,代理模式又是做什么的呢?
在日常开发中,我们常需要对某个或某些已经编写完成的类中的一些方法做一些代码的增强但又不需要改变代码的业务逻辑,在不改变原码的情况下这时我们就需要用到了代理模式,生成一个代理的对象来对某个或某些方法进行增强。
比如我要买一个进口的mac笔记本,而我想要便宜一些买到国外生产的不想要国产代加工,我需要找代购来帮我买,我要买mac笔记本就是本身的业务逻辑,而如何去卖到国外生产的代购,就是我的代理,这就是代理模式。
代理模式优缺点
优点:
业务类只需要关注业务逻辑本身,保证了业务类的重用性。
代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用。
缺点:
由于在客户端和真实主题之间增加了代理对象,因此会造成请求的处理速度变慢;
实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度。
代理模式类型
代理模式又分为两种类型:静态代理和动态代理,两者最本质的区别就是这些对代码的增强是在代码编译时期就已经完成还是在运行时完成。
静态代理模式
静态代理模式和动态代理最大的区别就是在编译时期就已经完成了代理对象的编写与封装,完成了我想要的业务增强,以上面的业务来代码示例:
//创建抽象对象接口(Subject):声明你(本来对象)需要让代购(代理对象)帮忙做的事
(买东西)
public interface Subject {
public void buyMac();
}
//创建本来的买东西对象(RealSubject),即”我“
public class RealSubject implement Subject{
@Override
public void buyMac() {
System.out.println(”买一台Mac“);
}
}
//创建代理对象类(Proxy),即”代购“,并通过代理类创建真实对象实例并访问其方法
public class Proxy implements Subject{
private RealSubject realSubject;
@Override
public void buyMac{
//引用并创建真实对象实例,即”我“
realSubject = new RealSubject();
//调用真实对象的方法,进行代理购买Mac
realSubject.buyMac();
//代理对象额外做的操作
this.WrapMac();
}
public void WrapMac(){
System.out.println(”用盒子包装好Mac笔记本“);
}
}
//输出结果
public class ProxyPattern {
public static void main(String[] args){
Subject proxy = new Proxy();
proxy.buyMac();
}
}
//买一台Mac
//用盒子包装好Mac
然而静态代理也有一些缺点:
一个静态代理只服务一种类型的目标对象,若要服务多类型的目标对象,则需要为每种目标对象都实现一个静态代理对象,在目标对象较多的情况下,若采用静态代理,则会出现 静态代理对象量多、代码量大,从而导致代码复杂,出现很多冗余代码等问题。。
要解决这个问题,就需要使用动态代理模式。
动态代理模式
动态代理的原理是设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSubject)相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现
即:在使用时再创建动态代理类 & 实例
静态代理则是在代理类实现时就指定与目标对象类(RealSubject)相同的接口
通过Java 反射机制的method.invoke(),通过调用动态代理类对象方法,从而自动调用目标对象的方法
而动态代理的在拥有代理模式的优缺点下也解决了一部分静态代理时所不方便的事情,即只需要一个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码
拥有更强的灵活性。
缺点则是:
效率低,相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法
动态代理又分为基于jdk的动态代理和基于cglib的动态代理,他们的区别则是jdk是基于接口的,而cglib则是基于类
jdk的动态代理
即让代理类实现与被代理类相同的接口,然后在实现某些方法时进行相应的增强(要生成代理对象需要实现InvocationHandler接口,重写invoke方法进行方法的增强,增强是基于反射进行的)
cglib的动态代理
即让代理类继承被代理类,同样也可以获取到所有被代理对象的方法,进而进行方法的增强。
代码示例:
参考链接:https://www.jianshu.com/p/5dc416ea58a2
// 1. 生成 动态代理对象
// 2. 指定 代理对象运行目标对象方法时需要完成的 具体任务
// 注:需实现InvocationHandler接口 = 调用处理器 接口
// 所以称为 调用处理器类
public class DynamicProxy implements InvocationHandler {
// 声明代理对象
// 作用:绑定关系,即关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke()
private Object ProxyObject;
public Object newProxyInstance(Object ProxyObject){
this.ProxyObject =ProxyObject;
return Proxy.newProxyInstance(ProxyObject.getClass().getClassLoader(),
ProxyObject.getClass().getInterfaces(),this);
// Proxy类 = 动态代理类的主类
// Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回
// 参数说明:
// 参数1:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
// 参数2:指定目标对象的实现接口
// 即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法
// 参数3:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象
}
// 复写InvocationHandler接口的invoke()
// 动态代理对象调用目标对象的任何方法前,都会调用调用处理器类的invoke()
@Override
public Object invoke(Object proxy, Method method, Object[] args)
// 参数说明:
// 参数1:动态代理对象(即哪个动态代理对象调用了method()
// 参数2:目标对象被调用的方法
// 参数3:指定被调用方法的参数
throws Throwable {
System.out.println("代购出门了");
Object result = null;
// 通过Java反射机制调用目标对象方法
result = method.invoke(ProxyObject, args);
return result;
}
}
//声明目标对象的抽象接口
public interface Subject {
// 定义目标对象的接口方法
// 代购物品
public void buybuybuy();
}
//声明目标对象类
// 小成,真正的想买Mac的对象 = 目标对象 = 被代理的对象
// 实现抽象目标对象的接口
public class Buyer1 implements Subject {
@Override
public void buybuybuy() {
System.out.println("小成要买Mac");
}
}
// 小何,真正的想买iPhone的对象 = 目标对象 = 被代理的对象
// 实现抽象目标对象的接口
public class Buyer2 implements Subject {
@Override
public void buybuybuy() {
System.out.println("小何要买iPhone");
}
}
//通过动态代理对象,调用目标对象的方法
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 创建调用处理器类对象
DynamicProxy DynamicProxy = new DynamicProxy();
// 2. 创建目标对象对象
Buyer1 mBuyer1 = new Buyer1();
// 3. 创建动态代理类 & 对象:通过调用处理器类对象newProxyInstance()
// 传入上述目标对象对象
Subject Buyer1_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer1);
// 4. 通过调用动态代理对象方法从而调用目标对象方法
// 实际上是调用了invoke(),再通过invoke()里的反射机制调用目标对象的方法
Buyer1_DynamicProxy.buybuybuy();
// 以上代购为小成代购Mac
// 以下是代购为小何代购iPhone
Buyer2 mBuyer2 = new Buyer2();
Subject Buyer2_DynamicProxy = (Subject) DynamicProxy.newProxyInstance(mBuyer2);
Buyer2_DynamicProxy.buybuybuy();
}
}
打印结果: