Java 设计模式——代理模式(Proxy Pattern)

概念:一个类代表另一个类的功能。这种类型的设计模式属于结构型模式(假装写个概念,别处Copy过来的,反正光看这句话有点云里雾里的,我还是喜欢从思路出发,能举一反三才算是理解)。

假如我们有这样一个需求,我们现在有一个已经上线的车票购买系统,购票实现代码如下:

public class StationBuyTicket implements IBuyTicket {

    @Override
    public void buyTicket(double price) {
        System.out.println(price + " yuan for a ticket");
    }
}


StationBuyTicket stationBuyTicket = new StationBuyTicket();
stationBuyTicket.buyTicket(4.3);

我们需要为其添加是否购满足票条件判断,账户状态是否可以购票和日志统计等等功能已满足新的业务需求,我们该怎么做?

思路1、在以上buyTicket方法中,添加一大堆If else进行判断,如果条件满足继续购买,不满足直接返回。

这种方式能实现需求吗?  答案是不确定,且不说代码结构和干净的问题,如果这个接口代码你随意修改,没问题完全可以,但是如果这个购买接口的代码是三方的库,源码你没法修改呢? 

思路2、既然源码没法修改,那我在调用此接口的时候进行判断,那就没问题了吧。

如果你这样想,恭喜你,方向对了。这里重点考虑的不是三方库我们没法修改,应该考虑的是解耦,减少不相关模块的耦合度,这只是一种编程思想,如果解耦了,以后修改或扩展购买接口,我们将更加方便。

注:高内聚,低耦合有助于程序的维护,单独的功能做成独立模块,整个系统由一个个模块通过接口组合而成,如果需要更新或者修改局部功能,只要修改一个接口的一个模块,不影响整个系统其他功能使用。

一、静态代理

接着以上的思路,我们需要做的是在接口外层加一层封装,以实现新增的日志流程等和原有的购买实现解耦。这里我们就可以用到代理模式了。举个例子,代理模式和车站售票和车票代卖点类似,车站卖车票和代理站卖票,代理站和车站卖的票是相同的,但是代理站还可以在卖票的同时,收取服务费等。车票和价格就相当于软件上的需求,所以代理的功能是大于被代理的功能的。毕竟如果功能相同,代理且不是多此一举。下面就用车票售卖来实现代理模式。

首先,既然是代理,那么代理需要卖和车站一样的车票,软件上来说就是功能需要一样,然后代理在车站的的基础上加点额外服务,这里我们就打个日志吧,那么在这种情况下,继承相同的父类是很好的选择

public interface IBuyTicket {

    /**
     * 购票
     */
    void buyTicket(double price);
}

其次,继承接口实现车站代码

public class StationBuyTicket implements IBuyTicket {

    @Override
    public void buyTicket(double price) {
        System.out.println(price + " yuan for a ticket");
    }
}

第三,实现代理,代理也需要和车站有相同的接口实现,也要添加自己的功能需求

public class ProxyBuyTicket implements IBuyTicket {

    private IBuyTicket iBuyTicket;

    public ProxyBuyTicket(IBuyTicket iBuyTicket) {
        this.iBuyTicket = iBuyTicket;
    }

    @Override
    public void buyTicket(double price) {
        //买票日志处理
        System.out.println("Log: start by ticket.");
        iBuyTicket.buyTicket(price);
        System.out.println("Log: end by ticket.");
    }
}

最后,该组装我们的代理了

StationBuyTicket stationBuyTicket = new StationBuyTicket();
ProxyBuyTicket proxyBuyTicket = new ProxyBuyTicket(stationBuyTicket);
proxyBuyTicket.buyTicket(4.3);

=============================输出结果=================================

Log: start by ticket.
4.3 yuan for a ticket
Log: end by ticket.

完成,看最后的输出结果,既调用的厂家的接口,又增加了代理商自己的需求。

总结思考:优秀的代码总会有良好的扩展性,下面来尝试一下增加其它种类的车票或者转化为机票呢。怎么操作?  增加新种类车票,修改父接口,修改代理和车站实现类。  新增机票呢,就需要重新创建父类接口,车站和代理代码了,这样扩展起来就略显复杂。

二、动态代理

那么,有没有其它方案简化这种新增功能的复杂度呢? 答案肯定是有的,Java 已经提供了Proxy类给予了我们很好的支持,这就是Java的动态代理。下面我们就来实现Java的动态代理怎么实现

首先,Proxy类实现必须是要提供接口,这里我们可以使用和上面相同的接口

其次,动态代理实现

public class DynamicBuyTicket {

    private IBuyTicket iBuyTicket;

    public DynamicBuyTicket(IBuyTicket iBuyTicket) {
        this.iBuyTicket = iBuyTicket;
    }

    public IBuyTicket getProxyInstance() {
        return (IBuyTicket) Proxy.newProxyInstance(iBuyTicket.getClass().getClassLoader(), iBuyTicket.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //买票日志处理
                System.out.println("DynamicProxy Log: start Buy Ticket.");
                Object result = method.invoke(iBuyTicket, args);
                System.out.println("DynamicProxy Log: end Buy Ticket.");
                return result;
            }
        });
    }

}

第三,动态代理调用和输出

IBuyTicket stationBuyTicket = new StationBuyTicket();
DynamicBuyTicket dynamicBuyTicket = new DynamicBuyTicket(stationBuyTicket);
IBuyTicket iBuyTicket = dynamicBuyTicket.getProxyInstance();
iBuyTicket.buyTicket(5.4);

=============================输出结果=================================

DynamicProxy Log: start Buy Ticket.
5.4 yuan for a ticket
DynamicProxy Log: end Buy Ticket.

总结思考:这样如果我们需要添加更多的机票,船票,是不是就更简单了。但这种方式需要通过继承的方式实现,但是如果我们使用的第三方库,且对方没有继承或者没有提供父类的API呢,怎么办?  Java动态代理就形同虚设了,没法使用。

三、其它三方代理

别急,Java Proxy实现不了的,还有很多牛逼哄哄的代理库可以用,比如CGLIB,javaassist,ASM。。。等等,具体实现方式,就动动手指自行百度了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值