java 系列(一) 动态代理(中)

从上节的学习想必大家都知道动态代理怎么写了,现在我们拓展一些,动态代理和装饰器模式,想必大家都很困惑。本小节主要介绍动态代理和装饰器模式的不同以及如何相互搭配。


java 系列: 动态代理(上)-一个简单的动态代理
java 系列: 动态代理(中)-与装饰器模式的搭配与使用
java 系列: 动态代理(下)-与Android相关的Hook技术


1.动态代理和装饰器模式

  • 1.装饰器模式的介绍:

    装饰器模式能够实现保持原有的接口,并为新的接口动态的添加功能。装饰器实现了java编程上的一个准则,多组合,少继承。
    装饰器的一个典型的例子是:java中的IO流的实现,大家在转过头来看javaIO流的思维导图,想必会恍然大悟了。
    例图:

    (ps:这个图会和我们下面将要讲的装饰器模式+动态代理的UML图一致,不再重复显示)

  • 2.两者区别

两者都可以在保持主业务流程下,对对象进行添加方法,增强这个类的使用。
但两者有着一些区别:

装饰器模式继承了接口,添加了新类,而代理没有对原代码有任何操作。
装饰器中的继承和创建新类,会造成出现大量的包装类,对于代码阅读是有困难的。

2.动态代理的装饰器模式搭配

为什么要这样搭配呢,想必大家在实践过程中使用代理都会遇到的问题–动态代理的扩展性。
动态代理是没有办法在源代理类上直接进行拓展的(否则的话全部的接口都会实现拓展的对象),但对于我们来说,并不需要,只是对某些委托类进行操作,这就不太这么方便操作了。所以这时候使用装饰器模式比较好的搭配使用,添加自定义的扩展。
相对于简单的动态代理,这时候要增加几个新的方法:
提供动态代理实例的静态工厂类(ProxyFactory)和抽象装饰器(Decorator)

  • 首次我们看抽象主题接口
    public interface AbstractSubject {
         void request();
    }
  • 真实主题类(委托类/被代理的对象)
    public class RealSubject implements AbstractSubject {
    public void request() {
        System.out.println("真实的方法-----RealSubject's request() ...");
    }
    }
  • 抽象装饰器类(一定要以自己的父类、父接口为一个属性)
    public class Decorator implements AbstractSubject {
    protected AbstractSubject subject = null;

    public Decorator(AbstractSubject subject) {
        this.subject = subject;
    }

    @Override
    public void request() {

    }
    }
  • 具体装饰器1(根据实际情况,我这里只写三个)
    public class ConcreteDecorator01 extends Decorator {

    public ConcreteDecorator01(AbstractSubject subject) {
        super(subject); //调用父类装饰类的构造器
    }

    /**
     * 覆盖继承树上的接口中的request()方法,用于装饰原对象
     */
    public void request() {
        System.out.println("第一层装饰 ... 装饰在原主题之前");
        super.subject.request();
    }
    }
  • 具体装饰器2(根据实际情况,我这里只写三个)
    public class ConcreteDecorator02 extends Decorator {

    public ConcreteDecorator02(AbstractSubject subject) {
        super(subject); //调用父类装饰类的构造器
    }

    /**
     * 覆盖继承树上的接口中的request()方法,用于装饰原对象
     */
    public void request() {
        super.subject.request();
        System.out.println("第二层装饰 ... 装饰在原主题之后");
    }
    }
  • 具体装饰器3(根据实际情况,我这里只写三个)
    public class ConcreteDecorator03 extends Decorator {

    public ConcreteDecorator03(AbstractSubject subject) {
        super(subject); //调用父类装饰类的构造器
    }

    /**
     * 覆盖继承树上的接口中的request()方法,用于装饰原对象
     */
    public void request() {
        super.subject.request();
        System.out.println("第三层装饰 ... 装饰在原主题之后");
    }
    }
  • 提供动态代理实例的静态工厂类(管理代理实现)
    public class ProxyFactory {
    /**
     * @param realSubject :指定需要代理的真实主题类的实例
     * @return proxy :代理的实例
     */
    public static AbstractSubject getProxy(AbstractSubject realSubject) {

        // 获得被代理类的类加载器,使得JVM能够加载并找到被代理类的内部结构,以及已实现的interface
        ClassLoader loader = realSubject.getClass().getClassLoader();

        // 获得被代理类已实现的所有接口interface,使得动态代理类的实例
        Class<?>[] interfaces = realSubject.getClass().getInterfaces();

        // 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 使用java.lang.reflect.Proxy类中的静态方法newProxyInstance()获得代理的实例
         *
         * loader : 被代理类的类加载器 interfaces :被代理类已实现的所有接口,而这些是动态代理类要实现的接口列表 handler
         * : 用被代理类的实例创建动态代理类的实例,用于真正调用处理程序
         *
         * return :返回实现了被代理类所实现的所有接口的Object对象,即动态代理,需要强制转型
         */
        AbstractSubject proxy = (AbstractSubject) Proxy.newProxyInstance(
                loader, interfaces, handler);

        return proxy;
    }
    }
  • 代理类
    public class DynamicProxy implements InvocationHandler {

    // 被代理类的实例
    Object obj = null;

    // 将被代理者的实例传进动态代理类的构造函数中
    public DynamicProxy(Object obj) {
        this.obj = obj;
    }

    /**
     * 覆盖InvocationHandler接口中的invoke()方法
     *
     * 更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构 的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到
     * 控制被代理对象的行为,下面的before、after就是我们可以进行特殊 代码切入的扩展点了。
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        /*
         * before :doSomething();
         */
        System.out.println("动态代理为真实主题添加一个方法  ...");
        Object result = method.invoke(this.obj, args);

        /*
         * after : doSomething();
         */
        return result;
    }
    }
  • client
    public class Client {
    public static void main(String[] args) {
        // 被代理类的实例
        AbstractSubject realSubject = new RealSubject();
        // 通过静态工厂获取动态代理的实例
        AbstractSubject proxy = ProxyFactory.getProxy(realSubject);
        // 装饰之前打印出该代理实例的名称
        System.out.println("装饰前:" + proxy.getClass().getName());
        // 装饰前使用代理实例进行原始操作
        //proxy.request();
        System.out.println("\n第一次装饰之后的效果如下:");
        proxy = new ConcreteDecorator01(proxy);
        System.out.println("\n名称:" + proxy.getClass().getName());
        //proxy.request();

        System.out.println("\n第二次装饰之后的效果如下:");
        proxy = new ConcreteDecorator02(proxy);
        System.out.println("\n名称:" + proxy.getClass().getName());
       // proxy.request();

        System.out.println("\n第三次装饰之后的效果如下:");
        proxy = new ConcreteDecorator03(proxy);
        System.out.println("\n名称:" + proxy.getClass().getName());
        proxy.request();
    }
    }
  • 总结

    通过上面的代码描述可以在proxy这个类已经是处理好之后的情况,在后面的大段代码都是标准的装饰器代码。
    运行之后:

    看好我画圈的地方,下面将

    AbstractSubject proxy = ProxyFactory.getProxy(realSubject);

方法去除,变成一个完整的装饰器模式,看看效果:

可以看到,结果完全相同,只有最初代理的对象不同,一个是经过代理产生的,一个是源接口。
对于这样的好处毋庸置疑:代理可以放在其他模块中,对于自定义的功能添加使用装饰器自由组合,对于统一的操作放在代理对象中,这样解耦的形式比较好。

3.结束语

其实设计模式之前的搭配有很多,只学单独的设计模式的概念并不是很大,很多注明的SDK都是很多设计模式相互搭配而来,请大家多多思考,下节讲动态代理的实际操作–Android中统一处理防双击/抖动。谢谢

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值