Java设计模式浅谈之:代理模式

194 阅读3分钟

一.代理模式


什么是代理模式?

控制访问: A对象不能直接调用B对象,通过创建C对象去调用B对象,那么C是B的代理对象,B是C的目标对象。

功能增强: 在C对象调用B对象的方法的同时,又在其中加入新的功能代码,使得原方法增强,也叫包了一层。

场景模拟:

小韭想买房要找中介,因为小韭只想付钱和拿房产证,其他买房复杂的事情交给中介去办。房产商想要完成交易,就要去找小韭的代理对象中介,最终的目标对象就是小韭。

中介接口
public interface BuyHouse {
    //付钱
    void pay();
    //拿房产证
    void getLicense();
}
买房人接口
public class BuyHousePeople implements BuyHouse {

    private String name;
    private Building building;

  public String getName() {
        return name;
    }

    public Building getBuilding() {
        return building;
    }
    public BuyHousePeople(String name, Building building) {
        this.name = name;
        this.building = building;
    }

    public void pay() {
        System.out.println(name + "给" + building.getName() + "楼盘付钱");
    }

    public void getLicense() {
        System.out.println("户主:" + name + "房产证上的楼盘地址:" + building.getAddress());
    }
}
中介
public class Proxy implements BuyHouse {

    private BuyHousePeople buyHousePeople;

    public Proxy(Building building,String name) {
         buyHousePeople = new BuyHousePeople(name,building);
    }

      public void pay() {
        System.out.println("中介开车接"+buyHousePeople.getName()+"去"+buyHousePeople.getBuilding().getAddress());
        buyHousePeople.pay();

    }

    public void getLicense() {
        System.out.println("中介帮"+buyHousePeople.getName()+"整理资料给开发商");
        buyHousePeople.getLicense();
    }
}
开发商
public class Building {

    private String name;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

开始交易
public class StartBuyHouse {

    public static void main(String[] args) {

        Building building = new Building();
        building.setName("山水·拾光");
        building.setAddress("长安航天城");
        Building building2 = new Building();
        building2.setName("万科金域华府");
        building2.setAddress("经开大道");

        Proxy proxy = new Proxy(building,"小韭");
        proxy.pay();
        proxy.getLicense();

        Proxy proxy2 = new Proxy(building2,"小李");
        proxy2.pay();
        proxy2.getLicense();
    }
}

输出
中介开车接小韭去长安航天城
小韭给山水·拾光楼盘付钱
中介帮小韭整理资料给开发商
户主:小韭房产证上的楼盘地址:长安航天城
中介开车接小李去经开大道
小李给万科金域华府楼盘付钱
中介帮小李整理资料给开发商
户主:小李房产证上的楼盘地址:经开大道

上面的代码我们能看出:

1. 在买房人(目标对象)在不自我学习买房知识的情况下(不改变原本代码),中介(代理对象)的使用使得买房人(目标对象)能力得到拓展,并完成买房。
2. 以后不管谁买房,只需要找中介(相同的能力代码抽取出来使得代码复用增强)。

   看到这里有没有感觉似曾熟悉,就是日志打印的时候是不是不需要每个方法中都加日志打印的代码? 判断接口权限的时候是不是不需要每个接口去写权限校验代码?这个就是AOP,面向切面编程,一种横向的代码增强模式,但是AOP使用的是动态代理模式。

二.动态代理


为什么要动态代理?
  1. 如果项目中的目标类比较多,那随之而来的代理类也会逐渐增多。

  2. 一旦接口中的方法有改动(返回值,参数类型等)都需要目标类和代理类的改变。

什么是动态代理?

为了解决上述静态代理缺点,在Java运行过程中,利用反射动态的创建目标对象,并且完成目标对象的调用以及增强的方式,叫动态代理。

我们可以改造上面的例子使用JDK自带动态代理:

public class DynamicProxy implements InvocationHandler {

    private Object object;

    public  Object get(Object o){
        this.object=o;
        return Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String name = method.getName();
        if (name.equals("pay")){
            System.out.println("中介帮忙pay..");
        }
        if (name.equals("getLicense")){
            System.out.println("中介帮忙getLicense..");
        }
        return method.invoke(object,args);
    }

    public static void main(String[] args) {
        BuyHousePeople buyHousePeople = new BuyHousePeople("小韭", new Building("山水·拾光", "长安航天城"));

        BuyHouse buyHouse = (BuyHouse)new DynamicProxy().get(buyHousePeople);

        buyHouse.pay();

        buyHouse.getLicense();

        BuyHousePeople buyHousePeople1 = new BuyHousePeople("小韭", new Building("科金域华府", "经开大道"));

        BuyHouse buyHouse1 = (BuyHouse)new DynamicProxy().get(buyHousePeople1);

        buyHouse1.pay();

        buyHouse1.getLicense();


    }
}

输出:

中介帮忙getLicense..
户主:小韭房产证上的楼盘地址:长安航天城
中介帮忙pay..
小韭给科金域华府楼盘付钱
中介帮忙getLicense..
户主:小韭房产证上的楼盘地址:经开大道