Java设计模式——代理模式

大家好,我是会冒泡的可乐。

最近会冒泡的可乐的女神要生日了,所以我决定通过代购买个拽鞋当生日礼物(这半个月又白忙活了😭),在下

单完的一瞬间,突然我发现,代购和我们Java中的静态代理模式很像啊,我顾不上买鞋的心痛,马上码了这篇文

章。

我先放一张静态代理的结构图:

image-20220413163633038

上图可以看到,一共有三个角色:

  1. 主题角色(Subject):可以是抽象类也可以是接口,是一个最普通的业务类型定义;
  2. 真实主题角色(RealSubject):被代理的角色,是业务逻辑的具体执行者;
  3. 代理角色(Proxy):它负责对真实主题角色的应用,并且在真实主题角色处理理完毕前后做预处理和善后处理工作;

现在不太理解不急,我们往下看。

一、静态代理

1. 代购

好啦,正式开始讲代购啦,咳咳。

我们都知道,一般女生都喜欢名牌包包,但名牌包包有个特点,国外的款式与价格都比国内有优势,所以以前很多多金的妹子都要亲自跑到国外的包包商店买。但是自己去买,不仅费飞机票,还费时间,因此聪明的中国人看到商机,衍生出了一个新的职业:代购。

有了代购,妹子不需要亲自去买了,只需要找代购买了(这里很关键,妹子是找的代购小哥买的,但实际上代购小哥还是需要去到包包商店买,毕竟代购小哥自己没货啊!因此最终提供买包服务的不是代购小哥,而是包包商店!)

我们使用代理模式将这个代购的场景用代码描述出来。

我们首先创建一个包包商店的接口(所有包包商店都需要符合这个包包商店接口的规范),接口里面定义了个buyBag的方法:

public interface IBagShop {
    void buyBag(String num);
}

接着定义一个包包商店的实现类,这个包包商店类实现了包包商店接口,因此提供了真实的买包服务:

public class BagShop implements IBagShop {
    @Override
    public void buyBag(String money) {
        System.out.println("买了一个价值"+money+"限量款包包!");
    }
}

最后定义我们的代购小哥,也要实现包包商店接口(毕竟也是需要提供买包服务给妹子的),且需要引用包包商店类,因为他最终还是需要自己去到包包商店买包包:

public class PurchasingAgent implements IBagShop {
    private IBagShop bagShop;

    public PurchasingAgent(IBagShop bagShop) {
        super();
        this.bagShop = bagShop;
    }

    @Override
    public void buyBag(String num) {
        // 调用包包商店的买服务
        bagShop.buyBag(num);
    }
}

三个主要类就完成啦,是不是很简单?没有?那我再给你们看下面这张图,你们就一目了然了:

MESA Monitor

最后我们再写个“妹子”类测测吧:

public class Girl {
    public static void main(String[] args) {
        IBagShop bagShop = new BagShop();
        PurchasingAgent purchasingAgent = new PurchasingAgent(bagShop);
        purchasingAgent.buyBag("10万");
    }
}

MESA Monitor

这个妹子成功买到这么贵的包包了!

2. 优点

那代理模式有啥优点呢?

  1. 由代理对象来间接访问目标对象,避免了直接访问目标对象给系统带来复杂杂性;
  2. 通过代理对象对原有对象增强。

第一点我们就不解释了,主要看第二点。

代购小哥尝到了甜头以后,就想吸引更多的妹子来找他买包,于是决定提供售前与售后服务,售前是提供与包包商店的服务员砍价的服务,售后则是附送一些小礼品服务,妹子们看到代购小哥的服务这么好,于是也介绍其他姐妹们来找代购买东西了。你看,这就是代购小哥对包包商店业务的增强。

那如何在代码上体现呢?因为是代购小哥提供的服务,因此我们找到代购PruchasingAgent类,在其buy方法中做修改即可:

public class PurchasingAgent implements IBagShop {
    private IBagShop bagShop;

    public PurchasingAgent(IBagShop bagShop) {
        super();
        this.bagShop = bagShop;
    }

    @Override
    public void buyBag(String num) {
        // 售前服务:砍价
        System.out.println("代购砍了50%的价格,好厉害啊!");
        // 调用包包商店的买服务
        bagShop.buyBag(num);
        // 售后服务:送小礼品
        System.out.println("代购送了价值5元的小礼品,真抠");
    }
}

再次启动“妹子”类测试,测试结果为:

MESA Monitor

从上面例子可以看到,代购小哥通过增加售前售后服务增强了包包商店的买包服务业务,因此可以看到静态代理最大的优点就是:通过代理对象对原有业务进行增强。

3. 缺点

静态方法不满足设计模式原则中的开闭原则(设计模式一共有六大原则,而开闭原则是最重要的一个!纳尼,怪不得会有动态代理)。

那啥是开闭原则?程序对外扩展开放,修改关闭,换句话说,要增加新的功能时,我们可以增加新的模块代码来实现新的需求,但不能修改原有的代码来实现新需求。我们就继续通过代购来看下代理模式如果破坏了开闭原则。

经过一段时间,有妹子问代购小哥是否可以在他这里买鞋子,代购小哥虽然没有,但是代购小哥知道鞋子商店在哪啊,于是就接了这单,此后,代购小哥不仅仅代购包包,还代购鞋子了。

为了增加代购小哥的业务,我们如何修改代码呢?

首先我们需要定义一个新的鞋子商店接口,里面定义了一个购买鞋子的方法,然后再建立个实现了鞋子商店接口的鞋子商店类,并实现购买鞋子的方法,这里还是满足开闭原则的,毕竟是新建类和接口嘛。然后最头疼的地方来了,代理类PurchaingAgent需实现鞋子商店接口,且需要引入鞋子商店类,并实现购买鞋子的方法,这些都对源代码进行大量的改动,如果以后代购小哥还想扩展业务,比如代购吃的,代购奶粉,那就要继续改动,这样就违背开闭原则了。

4. 总结

最后,我再来总结下:

其实,计算机中的许多思想也是通过生活而来,特别是设计模式,我们可以在生活中找到许多映射。静态代理其实就是代理类通过引用被代理对象去执行方法,当我们需要对该方法前后进行其他处理时,我们只需要在代理类中进行更改就行。

二、动态代理

1.什么是动态代理

动态代理简单来说就是在程序执行过程中,创建代理对象,通过代理对象执行方法,给目标类的方法增加额外的功

能,也叫做功能增强。

2.jdk动态代理

  • 首先我们需要有一个目标类,在目标类的基础上通过动态代理实现功能增强

  • 创建InvocationHandler接口的实现类,在这个类中实现invoke方法,在invoke方法中实现给目标类的方法

    增强功能

  • 通过JDK中的Proxy创建代理,通过代理调用目标类中的方法,实现功能增强

3.代码实现

创建一个接口:

public interface SomeService {
    void doSome();
    void doOther();
}

目标类实现这个接口:

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("执行doSome");
    }

    @Override
    public void doOther() {
        System.out.println("执行doOther");
    }
}

创建一个工具类,里面是增强的功能:

public class ServiceTools {

    public static void doLog(){
        System.out.println("方法的执行时间:" + new Date());
    }

    public static void doTrans(){
        System.out.println("方法执行完毕,提交事务");
    }

}

创建InvocationHandler接口的实现类

public class MyInvocationHandler implements InvocationHandler {
    //目标对象
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    //通过代理对象执行方法时,会调用执行这个invoke()方法

    /**
     *
     * @param proxy 代理类实例
     * @param method 被代理的方法
     * @param args 方法的参数数组
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("执行MyInvocationHandler中的invoke方法");

        //打印被调用的目标类中的方法名
        System.out.println(method.getName());

        Object res = null;

        //此方法是通过动态代理实现的增强的功能
        ServiceTools.doLog();

        //执行目标类方法,通过Method实现
        res = method.invoke(target, args);

        //此方法是通过动态代理实现的增强的功能
        ServiceTools.doTrans();

        //返回目标方法的执行结果
        return res;
    }
}

通过Proxy创建代理

public class MyApp {
    public static void main(String[] args) {
        //创建目标对象
        SomeService target = new SomeServiceImpl();

        //创建InvocationHandler对象
        InvocationHandler handler = new MyInvocationHandler(target);

        //使用Proxy创建代理
        SomeService proxy = (SomeService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler
        );

        //通过代理执行方法,会调用handle中的invoke()方法
        proxy.doSome();
    }
}

运行结果如下:

我是我,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

posted @ 2022-04-14 15:56  colaer  阅读(119)  评论(0)    收藏  举报