代理模式与装饰模式到底哪家强

目录

一、前言

二、JDK动态代理模式

1、开发中使用的组件及其方法

2、上面已经说了,代理类会委派给InvocationHandler接口的实现类去处理任务,任务逻辑必经invoke()方法,那看看源码中它是什么样子的:

3、实验数据

3.1、Subject接口

3.2、Subject接口的实现类RealSubject

3.3、InvocationHandler的实现类,Cache调用处理器

3.4、测试类

3.5、控制台打印结果:

4、增加InvocationHandler的一个实现类SyCache

5、修改补充JDKProxyTest

6、总结

三、装饰模式

1、装饰模式可以解决哪些问题

2、从类图走近装饰模式

2.1、Component组件接口

2.2、CorrectComponent具体组件实现类

2.3、CorrectDecorator具体装饰器类1

2.4、CorrectDecorator2具体装饰器类2

2.5、Condition辅助条件类

2.6、DecoratorWrapper装饰器包装类

2.7、 测试类

2.8、测试结果​编辑 

2.9、总结

四、动态代理与装饰模式有什么不同


一、前言

    设计模式这个东西,作为程序员还是需要掌握一些的。小到软件中级设计师考试、面试、读框架源码,大到日常开发、系统架构等都会碰到。而笔者也是早在上大学的那段时间,已经对设计模式有了一知半解的认识。

    JDK动态代理,在源码的世界里扮演的角色格外重要,如MyBatis的日志模块、binding模块等,Spring的AOP等。在日常开发中,你也可以捕捉到它的身影。使用代理的一个重要目标是控制对被代理类的访问,可以在代理类中做一些前置/后置处理,如:缓存日志事务权限等等。使用JDK动态代理的前提是:被代理类必须是一个接口或者其实现类。如果被代理类不是接口或者接口实现类,而你又想使用动态代理,可考虑使用CGLIBJavassist技术。这是一项硬技能,好的Java工程师、架构师都必须具备的知识。

    装饰模式可以动态的为对象添加功能,这是一个经典的概论。日常开发中可能不会经常碰到,但是在源码里面你还是可以捕捉到它的身影。

   JDK动态代理模式与装饰模式其实历来都有很多争议,许许多多的学者都认为它们没有什么多大区别,甚至直呼它们根本就是同一回事。今天就从实验角度探讨一下它们的区别,正如邓小平爷爷所说:“实践是检验真理的唯一标准”。

二、JDK动态代理模式

为什么代理模式有这么多种,而这里只是提到其中一种。因为在日常开发中,常见的就是使用JDK动态代理。感兴趣的读者可以自行去研究研究代理模式的其它种类。

1、开发中使用的组件及其方法

开发中用到的JDK动态代理组件为InvocationHandler接口以及Proxy类,它们的职责如下表:

类  类的职责
Proxy 负责生成代理类并关联调用处理器(InvocationHandler接口的实现类)
InvocationHandler接口负责处理代理的逻辑,如:缓存日志事务权限等等

1.1、描述

Proxy生成的代理类是不处理逻辑的,抽象又很真实你连代理类体都没看到如何写逻辑,但是它会委派给调用处理器invoke()方法处理逻辑,这也就是为什么多了个InvocationHandler接口及其实现类的原因。我们都知道,要调用一个类的非静态方法,一般都得经过它的实例去调用方法。好办,Proxy生成的代理类不处理逻辑,而委托给调用处理器的invoke()方法处理,那么要调用调用处理器的方法,你至少要拿到它的实例。故Proxy的newProxyInstance()方法(这个方法是开发中经常用到的)中就得把调用处理器的实例传入进来了。我们来看看源码中这个方法有哪些参数:

源码:


    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {

方法职责是创建代理类实例,并且代理类关联接管处理逻辑任务的调用处理器。

loader:类加载器,负责加载代理类。

interfaces:数组类型,表示提供了一组接口对象数组,那么代理类就实现了这些接口的所有方法并可调用。

h:InvocationHandler接口类型,代理类会通过InvocationHandler实现类的实例去调用invoke()方法处理逻辑。

2、上面已经说了,代理类会委派给InvocationHandler接口的实现类去处理任务,任务逻辑必经invoke()方法,那看看源码中它是什么样子的:

/**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     *
     * @param   proxy the proxy instance that the method was invoked on
     *
     * @param   method the {@code Method} instance corresponding to
     * the interface method invoked on the proxy instance.  The declaring
     * class of the {@code Method} object will be the interface that
     * the method was declared in, which may be a superinterface of the
     * proxy interface that the proxy class inherits the method through.
     *
     * @param   args an array of objects containing the values of the
     * arguments passed in the method invocation on the proxy instance,
     * or {@code null} if interface method takes no arguments.
     * Arguments of primitive types are wrapped in instances of the
     * appropriate primitive wrapper class, such as
     * {@code java.lang.Integer} or {@code java.lang.Boolean}.
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

参数描述:

proxy:被代理的类。

method:调用时被代理的类的方法的Method对象。

args:调用时被代理的类的方法的参数。

3、实验数据

3.1、Subject接口

public interface Subject {
 
    String doSomething();
}

3.2、Subject接口的实现类RealSubject

public class RealSubject implements Subject {
 
    @Override
    public String doSomething() {
        System.out.println("我是真实的Object");
        return "我是RealSubject";
    }

}

3.3、InvocationHandler的实现类,Cache调用处理器

 
package com.ceam.designer.proxy.jdk;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
public class Cache implements InvocationHandler {
 
    /**
     * 这里封装真实类一般可以是:Object类型、接口类型、Class类型
     */
//    private Object object;
//
//    Cache(Object object) {
//        this.object = object;
//    }
 
    private Subject object;
 
    /**
     * 初始化字段,method.invoke(object, args)中会用到该字段
     * 
     * @param object 真实对象
     */
    Cache(Subject object) {
        this.object = object;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----Before handler----");
 
        // 调用真实对象的方法,返回值result
        Object result = method.invoke(object, args);
        System.out.println((String)result);
 
        System.out.println("----After handler----");
 
        // 返回返回值
        return result;
    }
 
}

3.4、测试类

package com.ceam.designer.proxy.jdk;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
 
public class JDKProxyTest {
 
    public static void main(String[] args) {
        // 声明父类创建子类
        Subject subject = new RealSubject();
        // 封装真实的类
        InvocationHandler cache = new Cache(subject);
        // 生成代理实例,关联调用处理器
        /**
         * 关于入参的第一个第二个参数,一般是:第一个为接口的类类加载器,第二个为new Class[]{接口的Class}
         */
        Subject proxy = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                new Class[]{Subject.class}, cache);
        String result = proxy.doSomething();
        System.out.println("返回结果:" + result);
 
        System.out.println("***********************************");
 
        Subject proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                subject.getClass().getInterfaces(), cache);
        proxy2.doSomething();
    }
 
}

3.5、控制台打印结果:

4、增加InvocationHandler的一个实现类SyCache

package com.ceam.designer.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class SyCache implements InvocationHandler {

    /**
     * 这里封装真实类一般可以是:Object类型、接口类型、Class类型
     */
//    private Object object;
//
//    Cache(Object object) {
//        this.object = object;
//    }

    private Subject object;

    /**
     * 初始化字段,method.invoke(object, args)中会用到该字段
     *
     * @param object 真实对象
     */
    SyCache(Subject object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----SyCache Before handler----");

        // 调用真实对象的方法,返回值result
        Object result = method.invoke(object, args);
        System.out.println((String)result);

        System.out.println("----SyCache After handler----");

        // 返回返回值
        return result;
    }

}

5、修改补充JDKProxyTest

package com.ceam.designer.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class JDKProxyTest {

    public static void main(String[] args) {
        // 声明父类创建子类
        Subject subject = new RealSubject();
        // 封装真实的类
        InvocationHandler cache = new Cache(subject);
        // 生成代理实例,关联调用处理器
        /**
         * 关于入参的第一个第二个参数,一般是:第一个为接口的类类加载器,第二个为new Class[]{接口的Class}
         */
        Subject proxy = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                new Class[]{Subject.class}, cache);
        String result = proxy.doSomething();
        System.out.println("返回结果:" + result);

        System.out.println("***********************************");

        Subject proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                subject.getClass().getInterfaces(), cache);


        // 封装真实的类
        InvocationHandler syCache = new SyCache(subject);
        proxy2 = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                subject.getClass().getInterfaces(), syCache);
        proxy2.doSomething();
    }

}

打印结果:

6、总结

 无论从idea中的代码,还是打印的结果都可以看出:使用JDK动态代理,你无法让几个InvocationHandler接口的实现类自由组合,只能单独的使用其中一个为代理类提供服务。

三、装饰模式

1、装饰模式可以解决哪些问题

    在实践生产中,新需求总是不断出现的。当有新的需求时,就需要为某些组件添加新的功能来满足这些需求。我们可以直接的修改已有的组件代码并添加新的功能,这很明显会破坏已有组件的稳定性,修改完毕后,整个组件还需要重新进行测试,才能上线使用。这种做法明显违反了“开放-封闭”原则。

    装饰模式可以帮助我们解决上述问题,装饰模式可以动态地为对象添加功能,它是基于组合方式实现功能的。在实际开发中,业界都是推荐使用组合的方式扩展系统的功能,而少用继承的方式。请读者们记住一句话:组合优于继承

2、从类图走近装饰模式

                                                                 图2-1 

Component(组件):组件接口定义了全部组件实现类所有装饰器实现的行为。

CorrectComponent(具体组件实现类):具体组件实现类实现了Component(组件)接口。

Decorator(装饰器):所有装饰器的父类,它是一个实现了Component接口的抽象类,并在其中封装了一个Component对象,也就是被装饰的对象。而这个被装饰的对象只要是Component类型即可,这就实现了装饰器的组合和复用。但是它不一定会出现,有的设计也可以把它省略掉,而直接用具体的装饰器就行,我们得会灵活运用。

CorrectDecorator(具体装饰器):具体的装饰器实现类,它会向被装饰者添加某些功能,也就是新添加的需求功能在该实现类进行实现。当然具体装饰器可以有多个,而且它们之间都是相互独立的。

下面的类图是自己一些研究设计类图,主要部分还是上面的类图结构

                                                                图2-2

DecoratorWrapper从图2-2就可以看出,它的职责是负责创建装饰器的,具体代码请看下面2.6。

2.1、Component组件接口

package com.ceam.designer.decorator.two;

/**
 * Component.java
 * <p>
 * Copyright ©
 * All Rights Reserved.
 * Project: test-center
 * Description:
 *
 * @author : Administrator
 * History:
 * 2021-09-04 created by Administrator
 */
public interface Component {

    void doSomething();
}

2.2、CorrectComponent具体组件实现类

package com.ceam.designer.decorator.two;

/**
 * CorrectComponent.java
 * <p>
 * Copyright ©
 * All Rights Reserved.
 * Project: test-center
 * Description:
 *
 * @author : Administrator
 * History:
 * 2021-09-04 created by Administrator
 */
public class CorrectComponent implements Component {

    @Override
    public void doSomething() {
        System.out.println("我是具体组件");
    }
}

2.3、CorrectDecorator具体装饰器类1

package com.ceam.designer.decorator.two;

/**
 * CorrectDecorator.java
 * <p>
 * Copyright ©
 * All Rights Reserved.
 * Project: test-center
 * Description:
 *
 * @author : Administrator
 * History:
 * 2021-09-04 created by Administrator
 */
public class CorrectDecorator implements Component {

    private final Component component;

    CorrectDecorator(Component component) {
        this.component = component;
    }

    @Override
    public void doSomething() {
        component.doSomething();
        this.enchange();
    }

    private void enchange() {
        System.out.println("后置增强");
    }


}

2.4、CorrectDecorator2具体装饰器类2

package com.ceam.designer.decorator.two;

/**
 * CorrectDecorator2.java
 * <p>
 * Copyright ©
 * All Rights Reserved.
 * Project: test-center
 * Description:
 *
 * @author : Administrator
 * History:
 * 2021-09-04 created by Administrator
 */
public class CorrectDecorator2 implements Component {

    private final Component component;

    CorrectDecorator2(Component component) {
        this.component = component;
    }

    @Override
    public void doSomething() {
        component.doSomething();
        this.enchange();
    }

    private void enchange() {
        System.out.println("后置增强2");
    }
}

2.5、Condition辅助条件类

package com.ceam.designer.decorator.two;

/**
 * Condition.java
 * <p>
 * Copyright ©
 * All Rights Reserved.
 * Project: test-center
 * Description:
 *
 * @author : Administrator
 * History:
 * 2021-09-04 created by Administrator
 */
public class Condition {

    public static Boolean d1;
    public static Boolean d2;

    public Condition d1(boolean d1) {
        this.d1 = d1;
        return this;
    }

    public Condition d2(boolean d2) {
        this.d2 = d2;
        return this;
    }
}

2.6、DecoratorWrapper装饰器包装类

package com.ceam.designer.decorator.two;

/**
 * DecoratorWrapper.java
 * <p>
 * Copyright ©
 * All Rights Reserved.
 * Project: test-center
 * Description:
 *
 * @author : Administrator
 * History:
 * 2021-09-04 created by Administrator
 */
public class DecoratorWrapper implements Component{

    private static Condition condition;
    public static Boolean d1;
    public static Boolean d2;

    DecoratorWrapper() {
    }

    DecoratorWrapper(Condition condition) {
        this.condition = condition;
    }

    @Override
    public void doSomething() {
        this.en();
    }

    private void en() {
        Component component = new CorrectComponent();
        if (Condition.d1) {
            component = new CorrectDecorator(component);
        }
        if (Condition.d2) {
            component = new CorrectDecorator2(component);
        }
        component.doSomething();
    }

    public DecoratorWrapper d1(boolean d1) {
        this.d1 = d1;
        return this;
    }

    public DecoratorWrapper d2(boolean d2) {
        this.d2 = d2;
        return this;
    }
}

2.7、 测试类

package com.ceam.designer.decorator.two;

/**
 * Test.java
 * <p>
 * Copyright ©
 * All Rights Reserved.
 * Project: test-center
 * Description:
 *
 * @author : Administrator
 * History:
 * 2021-09-04 created by Administrator
 */
public class Test {

    public static void main(String[] args) {
//        Component component = new CorrectComponent();
//        component = new CorrectDecorator(component);
//        component = new CorrectDecorator2(component);
//        component.doSomething();
        Component component1 = new DecoratorWrapper(new Condition()
                .d1(false)
                .d2(false));
        component1.doSomething();

        System.out.println("2----------------------------------------");
        Component component2 = new DecoratorWrapper(new Condition()
                .d1(true)
                .d2(false));
        component2.doSomething();

        System.out.println("3----------------------------------------");
        Component component3 = new DecoratorWrapper(new Condition()
                .d1(true)
                .d2(true));
        component3.doSomething();

        System.out.println("4----------------------------------------");
        Component component4 = new DecoratorWrapper().d1(true).d2(true);
        component4.doSomething();
    }
}


2.8、测试结果 

2.9、总结

从实验结果可以看出:装饰模式可以动态组合,对被装饰对象添加功能。

四、动态代理与装饰模式有什么不同

动态代理与装饰模式有什么不同,笔者就不多说了,一切尽在实验数据当中,请读者仔细去研究研究。毕竟笔者也是下了很多功夫的,从源码知识,再经实验结论,都已经总结给读者了。如果有疑问的可以留言,笔者尽量为你解答。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卡布奇诺-海晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值