背景
代理模式是一种常用的设计模式,而对于代理,可根据代理类创建的时间点,分为静态代理和动态代理。
概念
- 真实对象:被代理的对象
- 代理对象:
- 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
代理类图
下面我们通过代码帮助大家理解静态代理和动态代理的实现方式
静态解决方案
静态代理
静态代理在程序运行之前,代理类的.class文件已经存在了。静态代理通常只代理一个类,并且要事先知道代理的哪个类。
简单实现
上面讲解了代理的一些基本原理,现在通过简单的例子帮助大家理解,比如张三想购买一台华为手机,如果自己去买价格要5000RMB,如果通过代理公司去买,,由于代理公司进行集中采购,能获得折扣价,价格在4800RMB.
首先,定义购买者接口类Buyer,里面有一个购买的方法 如下
/**
* @Author: dengcs
* @Date: 2020/6/24 10:40
* Comment:购买者接口
*/
public interface Buyer {
//购买方法
public void buy();
}
再编写一个购买者实现类ZanBuyer
/**
* @Author: dengcs
* @Date: 2020/6/24 10:47
* Comment:
*/
public class ZanBuyer implements Buyer {
private String name;
@Override
public void buy() {
System.out.println(name+"购买华为手机mate30 pro 5G");
}
public ZanBuyer(String name) {
this.name = name;
}
}
接着写一个代理公司类ProxyCommy,进行代理
/**
* @Author: dengcs
* @Date: 2020/6/24 10:54
* Comment:
*/
public class ProxyCommy implements Buyer {
ZanBuyer zanBuyer;
public ProxyCommy(ZanBuyer zanBuyer) {
if(zanBuyer.getClass()==ZanBuyer.class) {
this.zanBuyer = zanBuyer;
}
}
@Override
public void buy() {
zanBuyer.buy();
}
}
代理的代码基本上写完了,下面进行简单的测试,
/**
* @Author: dengcs
* @Date: 2020/6/24 10:59
* Comment: 测试类
*/
public class Test {
public static void main(String[] args) {
//被代理对象张三
Buyer buyer = new ZanBuyer("张三");
//代理公司,提交被代理对象张三的购买行为
Buyer proxy = new ProxyCommy( (ZanBuyer) buyer);
//代理公司进行购买
proxy.buy();
}
}
测试结果
张三购买华为手机mate30 pro 5G
可以看到,我们先声明了一个被代理对象对象buyer,接着生成代理对象proxy并将buyer 传给了代理对象,最后由代理对象代其完成购买行为,而不是上面的由buyer 直接提交。这就是代理模式。
那么你们会不会觉得奇怪,既然buyer可以直接购买,为什么还要增加一个代理类,这样反而增加了代码量。其实这样做的目的有两个,一是代码解耦,再就是代理对象可以在其中加入其他行为,而不需要修改原有对象的代码。如下,我们做如下代码增强功能
/**
* @Author: dengcs
* @Date: 2020/6/24 10:54
* Comment:
*/
public class ProxyCommy implements Buyer {
ZanBuyer zanBuyer;
public ProxyCommy(ZanBuyer zanBuyer) {
if(zanBuyer.getClass()==ZanBuyer.class) {
this.zanBuyer = zanBuyer;
}
}
@Override
public void buy() {
zanBuyer.buy();
System.out.println("团体采购,价格更优惠!");
}
}
测试结果为
可以看到,我们只在代理类中帮张三购买手机后,执行其他操作,便实现了我们想要的功能,并没有去修改我们原有的ZanBuyer类中的代码。这种操作也是使用代理模式的一个很大的优点。
动态解决方案
如果Buyer除了购买手机外,还能购买汽车等其它行为呢,这就需要用到动态代理方式了,对Buyer进行如下改造
/**
* @Author: dengcs
* @Date: 2020/6/24 10:40
* Comment:购买接口
*/
public interface Buyer {
public void buy();
public void buyCar();
}
ZanBuyer的改造
/**
* @Author: dengcs
* @Date: 2020/6/24 10:47
* Comment:
*/
public class ZanBuyer implements Buyer {
private String name;
@Override
public void buy() {
System.out.println(name+"购买华为手机mate30 pro 5G");
}
public ZanBuyer(String name) {
this.name = name;
}
@Override
public void buyCar() {
System.out.println(name+"购买一台比亚迪新能源汽车-汉");
}
}
把静态代理类ProxyCommy 改成 动态代理类DynProxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Author: dengcs
* @Date: 2020/6/24 10:54
* Comment:动态代理DynProxy
*/
public class DynProxy {
//设计一个类变量记住代理类要代理的目标对象
private Buyer buyer;// = new ZanBuyer("张三");
public DynProxy(Buyer buyer) {
this.buyer = buyer;
}
/**
* 设计一个方法生成代理对象
* @Method: getProxy
* @Description: 这个方法返回Buyer的代理对象:Buyer buyer = LiuDeHuaProxy.getProxy();//得到一个代理对象
* @return 某个对象的代理对象
*/
public Buyer getProxy(){
return (Buyer) Proxy.newProxyInstance(DynProxy.class.getClassLoader(), buyer.getClass().getInterfaces(), new InvocationHandler() {
/**
* InvocationHandler接口只定义了一个invoke方法,因此对于这样的接口,我们不用单独去定义一个类来实现该接口,
* 而是直接使用一个匿名内部类来实现该接口,new InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类
*/
/**
* 在invoke方法编码指定返回的代理对象干的工作
* proxy : 把代理对象自己传递进来
* method:把代理对象当前调用的方法传递进来
* args:把方法参数传递进来
*
* 当调用代理对象的person.sing("冰雨");或者 person.dance("江南style");方法时,
* 实际上执行的都是invoke方法里面的代码,
* 因此我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果调用的是代理对象的buy方法
if (method.getName().equals("buy")) {
System.out.println("这里是代理购买手机的");
return method.invoke(buyer, args); //代理对象调用真实目标对象的buy方法去处理用户请求
}
//如果调用的是代理对象的buyCar方法
if (method.getName().equals("buyCar")) {
System.out.println("这里是代理购买汽车的");
return method.invoke(buyer, args);//代理对象调用真实目标对象的buyCar方法去处理用户请求
}
return null;
}
});
}
}
测试Test 如下改造
/**
* @Author: dengcs
* @Date: 2020/6/24 10:59
* Comment: 测试类
*/
public class Test {
public static void main(String[] args) {
/*
//1、静态代理
//被代理对象张三
Buyer buyer = new ZanBuyer("张三");
//代理公司,提交被代理对象张三的购买行为
Buyer proxy = new ProxyCommy( (ZanBuyer) buyer);
//代理公司进行购买
proxy.buy();
*/
//2、动态代理
Buyer buyer1 = new DynProxy(new ZanBuyer("张三")).getProxy(); //获得代理对象
buyer1.buy(); //代理买手机
buyer1.buyCar();//代理买汽车
}
}
运行结果如下
至此,静态代理、动态代理基本讲完了。。
总结
其实代理模式最主要的就是有公共接口,一个代理类,代理类持有具体类的实例,代为执行具体类的实例方法。
-
代理模式其优点在于:代理模式实现使用者与真实处理者的分离,降低系统的耦合度;调用接口时,便于扩展一些业务无关的其他操作,不影响原系统。
-
缺点在于:增加类代理角色,性能上比直接使用低。
代码下载地址Demo
想了解更多设计模式,请关注我吧_