设计模式之代理模式


前言


代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。 为什么要采用这种间接的形式来调用对象呢? 一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。 思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法.

一、需求

假设:
我们有充值话费的功能,此系统已经运行 N 时间了,由于我们业务变动需增加一个查询余额功能后才能进行充值话费。
但是由于老系统充值代码出错率极低。如果增加新的东西可能会增添原系统的出错率。
此时我们就能用到代理模式了。(当然正常情况下不会出现假设的这种需求...因为在做之前肯定会查询余额的, 这里就是个举例方便大家理解这个思想)	

二、代理模式 (静态代理/动态代理)

1.静态代理

1.先来定义一个接口:

/**
 * @author aki
 * 创建充值话费接口类
 */
public interface PayTelephoneCharge {
    /**
     * 充值
     * @param  userId 用户Id
     * @param phone 电话
     * @param money 金额
     */
    void pay(Long userId,Long phone, Long money);

}

2.定义一个接口实现类


/**
 * @author aki
 * 微信充值话费实现类
 */
@Slf4j
public class PayTelephoneChargeImpl implements PayTelephoneCharge {
    /**
     * 充值
     *
     * @param phone 电话
     * @param money 金额
     */
    @Override
    public void pay(Long userId,Long phone, Long money) {
        log.info("我要充值话费了~");

    }
}

3.定义一个代理类

/**
 * @author aki
 * 充值话费代理类
 */
@Slf4j
public class PayTelephoneChargeProxy implements PayTelephoneCharge {

    private PayTelephoneCharge payTelephoneCharge;

    public PayTelephoneChargeProxy(PayTelephoneCharge payTelephoneCharge) {
        this.payTelephoneCharge = payTelephoneCharge;
    }

    /**
     * 充值
     *
     * @param phone 电话
     * @param money 金额
     */
    @Override
    public void pay(Long userId, Long phone, Long money) {
        log.info("充值前准备;查看余额还有多少钱.");
        payTelephoneCharge.pay(userId,phone, money);
        log.info("充值后;查看我的话费到账了吗或项目里做些日志之类的.");
    }
}

4.创建测试类


/**
 * @author aki
 * 测试->代理类
 */
@Slf4j
public class ProxyTest {

    public static void main(String[] args) {
        Long userId = 126001L;
        Long phone = 18100012345L;
        //价格单位(分)金额可以用Long类型,当然还有其他方式 如 BigDecimal money=new BigDecimal(50);等等  
        // 项目里金额不要用浮点数声明.(浮点数会导致无穷尽小数)
        Long money = 5000L;
        PayTelephoneChargeProxy payTelephoneChargeProxy = new PayTelephoneChargeProxy(new PayTelephoneChargeImpl());
        payTelephoneChargeProxy.pay(userId, phone, money);
    }
}

结果如下:
 充值前准备;查看余额还有多少钱.
 月初啦,我要充值话费了~
 充值后;查看我的话费到账了吗或项目里做些日志之类的.

我们可以看出代理模式的特点,代理类必须声明接口,任何实现该接口的类(实现类),都可以通过代理类进行代理,通用性更高。但是也有缺点,每一个代理类都必须实现一遍接口,如果接口增加方法,代理类也必须跟着修改。其次,代理类每一个接口对象对应一个实现类对象,如果实现类对象非常多,增加了代码维护的复杂度。

以下我们试着用动态代理来解决此问题

2.JDK动态代理

1.先来定义一个接口

/**
 * @author aki
 * 创建充值话费接口类
 */
public interface PayTelephoneCharge {
    /**
     * 充值
     * @param  userId 用户Id
     * @param phone 电话
     * @param money 金额
     */
    void pay(Long userId,Long phone, Long money);

}

2.定义实现类

/**
 * @author aki
 * 微信充值话费实现类
 */
@Slf4j
public class PayTelephoneChargeImpl implements PayTelephoneCharge {
    /**
     * 充值
     *
     * @param phone 电话
     * @param money 金额
     */
    @Override
    public void pay(Long userId,Long phone, Long money) {
        log.info("月初啦,我要去微信充值话费了~");

    }
}
/**
 * @author aki
 * 支付宝充值话费实现类
 */
@Slf4j
public class AlipayPayTelephoneChargeImpl implements PayTelephoneCharge {
    /**
     * 充值
     *
     * @param phone 电话
     * @param money 金额
     */
    @Override
    public void pay(Long userId, Long phone, Long money) {
        log.info("月初啦,我要去支付宝充值话费了~");

    }
}

3、定义一个动态代理类

/**
 * @author aki
 * 动态代理类
 */
@Slf4j
public class PayTelephoneChargeProxy implements InvocationHandler {
	//我们有多个实现类,都可能会使用.所有必须用Object或你有更好的办法.
    private Object object;
    
    public Object bindProxy(Object object) {
        this.object = object;
        //使用Proxy.newProxyInstance 返回某个代理类/对象
        //getClassLoader Java类装载器(加载器)可以通过这个加载器,在运行时将生成的代理类加载到JVM中.
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("充值前准备;查看余额还有多少钱.");
        Object invoke = method.invoke(object, args);
        log.info("充值后;查看我的话费到账了吗或项目里做些日志之类的.");
        return invoke;
    }
}

4.创建测试类


/**
 * @author aki
 * 测试->代理类
 */
@Slf4j
public class ProxyTest {

    public static void main(String[] args) {
        Long userId = 126001L;
        Long phone = 18100012345L;
        //价格单位(分)金额可以用Long类型,当然还有其他方式 如 BigDecimal money=new BigDecimal(50);等等
        // 项目里金额不要用浮点数声明.(浮点数会导致无穷尽小数)
        Long money = 5000L;
        PayTelephoneChargeJdkProxy payTelephoneChargeProxy = new PayTelephoneChargeJdkProxy();
         PayTelephoneJdkCharge payTelephoneCharge = (PayTelephoneJdkCharge) payTelephoneChargeProxy.bindProxy(new PayTelephoneChargeJdkImpl());
        //PayTelephoneJdkCharge payTelephoneCharge = (PayTelephoneJdkCharge) payTelephoneChargeProxy.bindProxy(new AlipayPayTelephoneChargeJdkImpl());
        payTelephoneCharge.pay(userId, phone, money);
    }

}

结果如下:
 充值前准备;查看余额还有多少钱.
 月初啦,我要去微信充值话费了~;
 充值后;查看我的话费到账了吗或项目里做些日志之类的.
---------------------
 充值前准备;查看余额还有多少钱.
 月初啦,我要去支付宝充值话费了~;
 充值后;查看我的话费到账了吗或项目里做些日志之类的.

我们看到动态代理类里使用 InvocationHandler接口只定义了一个invoke方法,因此对于这样的接口,我们不用单独去定义一个类来实现该接口。重复性功能更低。如不用此方式我们需要为N个实现类创建对应的N个代理类.

Proxy.newProxyInstance:返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序,生成了一个Proxy实例。
在这里插入图片描述
newProxyInstance里面参数可看上面代码注释这里就不在解释了.


总结

静态代理:可以在不修改老代码情况下,对代理目标的功能进行拓展。需要实现代理目标对象实现的接口,一旦代理目标所实现的接口有修改,目标对象与代理都需要维护,要解决此缺点,就必须用动态代理。

动态代理:代理类,不需要实现接口,代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。

如果不适用JDK API 还有一种方式:CGLIB 方式 此方式需引入Jar包. 接下来用一章来详细聊聊CGLIB。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值