什么是代理模式
先介绍两个概念:通用业务、核心业务
通用业务 指的是一些业务中,行为结果相同的子业务
核心业务 指的是一个业务中,特有的子业务,是其他业务没有的
例子: A想吃饭得完成业务1:买菜——>做菜——>吃饭——>处理厨余垃圾
B想吃饭得完成 业务2:买菜——>做菜——>吃饭——>处理厨余垃圾
业务1、业务2都可分为4个子业务,这四个子业务中买菜、做菜、处理厨余垃圾可以叫别人做(谁做结果都一样)、吃饭必须自己来(别人不能帮你把饭迟到你肚子里去),所以买菜、做菜、处理厨余垃圾就是通用业务,吃饭就是核心业务。
代理模式就是将通用业务交给代理类处理,其他委托类只负责自己的核心业务
代理模式实现如下:
客户想实现某个业务,直接调用接口的实现该业务的方法,实际是调用了代理类实现该业务的方法,代理类只实现通用业并调用委托类的对应方法实现核心业务。
优点:
- 职责清晰:委托类只需专注实现自己的核心业务、不用理通用业务
- 降低代码耦合
- 扩展性强
静态代理模式实现代码
背景:英雄都有Q、E技能,但英雄VN、英雄GL的Q、E技能的实现各不相同,但他们都有前摇、后摇
1.定义一个英雄接口
public interface Hero {
//Q技能
public void Q();
//E技能
public void E();
}
2.定义一个英雄VN类
public class VN implements Hero{
@Override
public void Q() {
System.out.println("薇恩(Q技能):闪避突袭");
}
@Override
public void E() {
System.out.println("薇恩(E技能):恶魔审判");
}
}
3.定义一个英雄GL类
public class GL implements Hero{
@Override
public void Q() {
System.out.println("盖伦(Q技能):致命打击");
}
@Override
public void E() {
System.out.println("盖伦(W技能):审判");
}
}
4.定义代理类
public class StaticProxy implements Hero{
private Hero hero; //委托对象
@Override
public void Q() {
System.out.println("技能前摇。。。");
hero.Q(); //核心业务
System.out.println("技能后摇。。。");
}
@Override
public void E() {
System.out.println("技能前摇。。。");
hero.E(); //核心业务
System.out.println("技能后摇。。。");
}
public void setHero(Hero hero) {
this.hero = hero;
}
}
5.测试
public static void main(String[] args) {
StaticProxy proxy = new StaticProxy();
proxy.setHero(new VN());//想调用VN的Q技能
proxy.Q();
System.out.println("==============");
proxy.setHero(new GL());//想调用GL的Q技能
proxy.Q();
}
运行结果:
技能前摇。。。
薇恩(Q技能):闪避突袭
技能后摇。。。
==============
技能前摇。。。
盖伦(Q技能):致命打击
技能后摇。。。
像技能前摇、技能后摇这些通用业务只需在代理类里实现,不必在每个英雄类中实现,降低代码耦合
动态代理模式实现代码
英雄接口、VN类、GL类都和静态代理一样
但还要定义个处理器类
public class ProxyHandle implements InvocationHandler{
private Object target;//委托对象
/**
* @param proxy 代理对象
* @param method 接口对应的方法对象
* @param args 方法参数
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("技能前摇。。。");
System.out.println(proxy.getClass()); //看看传入的对象是什么
Object ret = method.invoke(target, args); //核心业务
System.out.println(method.getDeclaringClass()); //看看声明该方法的类是什么
System.out.println("技能后摇。。。");
return ret;
}
public Object getProxyInstance() throws NullTargetException {
if(target==null)
throw new NullTargetException("未设置委托对象");
Class<?> clazz = target.getClass();
//返回一个代理类对象,该代理类实现了target实现的接口
return Proxy.newProxyInstance(
clazz.getClassLoader(), //类加载器
clazz.getInterfaces(), //类实现的接口
this); //处理器
}
public void setTarget(Object target) {
this.target = target;
}
}
测试代码
public static void main(String[] args) throws NullTargetException {
ProxyHandle proxy = new ProxyHandle();
proxy.setTarget(new VN()); //设置委托对象
Hero hero = (Hero)proxy.getProxyInstance(); //获得代理对象
//实际调用了处理器的invoke方法,并把接口对应的方法对象和方法参数还有代理对象传入invoke方法
hero.Q();
System.out.println(hero.getClass());
System.out.println("===========================");
proxy.setTarget(new GL());
hero = (Hero)proxy.getProxyInstance();
hero.Q();
System.out.println(hero.getClass());
}
输出结果;
技能前摇。。。
class com.sun.proxy.$Proxy0
薇恩(Q技能):闪避突袭
interface com.mode.dynamic.Hero
技能后摇。。。
class com.sun.proxy.$Proxy0
===========================
技能前摇。。。
class com.sun.proxy.$Proxy1
盖伦(Q技能):致命打击
interface com.mode.dynamic.Hero
技能后摇。。。
Proxy.newProxyInstance方法主要是动态创建一个实现了委托对象的父接口代理类对象,代理类重写的方法都是调用处理器的invoke方法,并把代理类对象,方法对象(委托对象父接口声明的方法),方法参数(委托对象父接口声明的方法的参数)传入invoke方法
上面背景是英雄GL和VN都是英雄的、都只有Q、E技能情况,假设英雄GL既是英雄又是草丛三贱客而草丛三贱客特有蹲草丛的技能。下面是静态代理和动态代理的修改:
新增TreeCheapGuest接口
public interface ThreeCheapGuest {
//蹲草丛
public void stayInGrass();
}
GL类更改:
静态代理更改:
动态代理更改:
动态代理与静态代理相比:
更大程度降低代码耦合
无需编写代理类,扩展性更强