您当前的位置:首页 > 计算机 > 编程开发 > 设计模式

【设计模式】26.结构型模式-代理模式(Proxy)

时间:02-01来源:作者:点击数:

一、描述

定义:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。代理模式分为静态代理、动态代理、CGLIB代理等方式,稍后会举例说明。

角色:

1.抽象对象:一般是来声明代理对象和被代理对象之间的公共方法,该类可以是接口,也可以是抽象类。

2.目标对象:又称为被代理类,是真正执行业务逻辑的类

3.代理对象:又称为代理类,此类中一般包含了目标对象的引用,因此具备了目标对象的代理权。并且可以对目标对象进行逻辑增强或控制性操作。

类图:

在这里插入图片描述

 从定义上来看,其实和中介者模式(行为模式)有相似之处,但是具体区分的话,其实在目的,角色划分上是不同的,

1.目的:中介者模式是为了解耦,将访问者与被访问者解耦,避免直接关联;代理模式是为了控制对某对象的访问。

2.角色:中介者模式分为四种角色:抽象中介类、具体中介类、抽象同事类、具体同事类;代理模式分为三种角色:抽象对象、目标对象、代理对象

二、优点

1.代理模式能将代理对象和目标对象分离

2.在一定程度上降低了系统的耦合,扩展性好

3.可以起到保护目标的作用

4.可以起到增强目标对象的作用

三、缺点

1.请求会先通过代理类,会降低请求效率

2.系统复杂度增加

四、适用场景

最常用的场景便是Spring 的AOP了,相信很多同学都用过。

五、示例

以“点外卖为例”,小王想点一份25块钱西红柿盖浇饭,但是不想出门,于是在外卖平台上点了某个饭店外卖,外卖平台上有满减,满20-2,于是小王只花了23块钱就得到了一份盖浇饭。

  分析一下角色分类:

  客户:小王;代理类:外卖平台;委托类:饭店

  依次举例不同代理分类的代码:

1.静态代理

(1)抽象下单类
public interface AbstractOrder {
    /**
     * 下单
     *
     * @return
     */
    Double placeOrder(Integer type);
}
(2)菜单枚举
@Getter
@AllArgsConstructor
public enum OrderList {
    XI_HONG_SHI_CHAO_DAN(1, "西红柿炒鸡蛋盖饭",new Double(25)),
    LA_JIAO_CHAO_ROU(2, "辣椒炒肉盖饭",new Double(20)),
    MU_XU_ROU(3, "木须肉肉盖饭",new Double(18));

    /**
     * 菜单类型
     */
    private Integer type;
    /**
     * 菜单名称
     */
    private String name;
    /**
     * 价格
     */
    private Double price;


    private static final Map<Integer, OrderList> MAP = Stream.of(OrderList.values())
            .collect(Collectors.toMap(OrderList::getType, Function.identity()));

    public static OrderList get(Integer type) {
        return MAP.get(type);
    }
}
(3)小王下单类
public class XiaoWangOrder implements AbstractOrder {
    @Override
    public Double placeOrder(Integer type) {
        OrderList orderList = OrderList.get(type);
        System.out.println("小王:我想点一份" + orderList.getName() + "(" + orderList.getPrice() + ")");
        return orderList.getPrice();
    }
}
(4)外卖平台
public class DeliveryPlatform implements AbstractOrder {
    //小王下单
    private XiaoWangOrder xiaoWangOrder;
    
    public DeliveryPlatform(XiaoWangOrder xiaoWangOrder) {
        this.xiaoWangOrder = xiaoWangOrder;
    }

    @Override
    public Double placeOrder(Integer type) {
        System.out.println("使用外卖平台点单:");
        Double money = xiaoWangOrder.placeOrder(type);
        //判断价格是否大于20元,若大于,优惠2元
        if (Double.compare(money, new Double(20)) > 0) {
            System.out.println("外卖平台已满20元,优惠2元");
            return money - 2;
        }
        return money;
    }
}
(5)测试
public class Client {
    public static void main(String[] args) {
        //将小王初始化到外卖平台,小王可以点单
        DeliveryPlatform platform = new DeliveryPlatform(new XiaoWangOrder());
        Double money = platform.placeOrder(OrderList.XI_HONG_SHI_CHAO_DAN.getType());
        System.out.println("最终消费金额:" + money);
    }
}

测试结果:

在这里插入图片描述

2.动态代理

抽象类、小王、枚举类不变,变化的是代理类,使用InvocationHandler与Proxy代理类处理,注意包全是java.lang.reflect下面的类。

(1)代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * DeliveryPlatform
 *
 * @author zhouxy
 * @date 2022/8/5
 **/
public class DeliveryPlatform implements InvocationHandler {
    private AbstractOrder abstractOrder;

    /**
     * 获取实例对象
     *
     * @param abstractOrder
     * @return
     */
    public AbstractOrder getInstance(AbstractOrder abstractOrder) {
        this.abstractOrder = abstractOrder;
        Class<?> clazz = abstractOrder.getClass();
        //将当前代理类加入到处理下单类的逻辑中,当有访问该abstractOrder的时候,会被invoke方法拦截处理。
        return (AbstractOrder) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object obj = method.invoke(abstractOrder, args);
        //根据返回结果处理数据
        return after((Double) obj);
    }

    public void before() {
        System.out.println("外卖平台开始点单");
    }

    public Double after(Double price) {
        //优惠金额大于20时,则优惠2元
        if (Double.compare(price, 20) > 0) {
            System.out.println("消费金额大于20,优惠2元");
            price = price - 2;
        }
        System.out.println("外卖平台点单完成");
        return price;
    }
}

其中,着重解释一下Proxy类,此类为代理类,使用到的为newProxyInstance方法,该方法的作用是:

Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.


百度翻译:返回指定接口的代理类实例,该接口将方法调用分派给指定的调用处理程序。

根据注释可以很清楚的看出该方法的用处,即将指定的调用程序DeliveryPlatform指定给getInstance方法中的abstractOder类中的方法,在访问该类时,会走DeliveryPlatform中的invoke方法。

(2)测试
public class Client {
    public static void main(String[] args) {
        DeliveryPlatform deliveryPlatform = new DeliveryPlatform();
        AbstractOrder xiaoWangOrder = deliveryPlatform.getInstance(new XiaoWangOrder());
        Double price = xiaoWangOrder.placeOrder(OrderList.XI_HONG_SHI_CHAO_DAN.getType());
        System.out.println("最终消费:" + price);
    }
}

测试结果:

在这里插入图片描述

3.CGLIB

(1)CGLIB实现代理类
public class DeliveryPlatformCGLIB implements MethodInterceptor {

    /**
     * 获取实例,是一个字节码增强器,可以用来为委托类创建代理
     *
     * @param clazz
     * @return
     */
    public Object getInstance(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        //处理委托类方法
        Object obj = methodProxy.invokeSuper(o, objects);
        return after((Double) obj);
    }

    public void before() {
        System.out.println("外卖平台开始点单");
    }

    /**
     * 优惠
     *
     * @param price
     * @return
     */
    public Double after(Double price) {
        //优惠金额大于20时,则优惠2元
        if (Double.compare(price, 20) > 0) {
            System.out.println("消费金额大于20,优惠2元");
            price = price - 2;
        }
        System.out.println("外卖平台点单完成");
        return price;
    }
}
(2)测试
public class Client {
    public static void main(String[] args) {
        DeliveryPlatformCGLIB cglib = new DeliveryPlatformCGLIB();
        XiaoWangOrder xiaoWangOrder = (XiaoWangOrder) cglib.getInstance(XiaoWangOrder.class);
        Double price = xiaoWangOrder.placeOrder(OrderList.XI_HONG_SHI_CHAO_DAN.getType());
        System.out.println("最终消费:" + price);
    }
}

测试结果:

在这里插入图片描述
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门