設計模式總結——代理模式以及java的動態代理

weixin_34162695發表於2018-12-24

定義

給目標物件一個代理物件,並由代理物件控制對目標物件的引用。聯想到生活中就像是海外代購

既然是代理,就說明他要做的事情要比你直接去做要做的多,這就聯絡到了方法的增強,也就聯絡到了AOP,面向切面。簡單講就是:繼承這個體系相當於是縱向,而AOP相當於是橫向。但是java自帶的增強只能增強介面中的方法,伺服器開發中spring對這點做的很好,無論是不是介面方法都可以對其進行增強。在Android中可以考慮使用cglib,這裡暫不討論。

這裡以一個汽車的啟動過程為例項來講解java的代理:

public interface ICar {
    //汽車啟動
    void start();
    //汽車執行
    void run();
    //汽車停止
    void stop();
}

//建立汽車的實現類
public class CarImpl implements ICar {
    @Override
    public void start() {
        System.out.println("CarImpl start");
    }

    @Override
    public void run() {
        System.out.println("CarImpl run");

    }

    @Override
    public void stop() {
        System.out.println("CarImpl stop");

    }
}

//建立測試類
public static void main(String[] args) {
        CarImpl car = new CarImpl();
           car.start();
           car.run();
           car.stop();
    }

複製程式碼

以上類的建立便完成了一個汽車從啟動到停止的一個模擬過程,這是思考一個問題,在汽車啟動的時候需要先檢查車,在啟動成功了之後要提醒司機注意天氣,那麼在現有的邏輯上怎麼處理?這裡給出兩種解決辦法,1.可以採用裝飾者模式對汽車的實現類進行包裝。2.因為start是介面方法,可以採用java的動態代理

  • 可以採用裝飾者模式對汽車的實現類進行包裝
public class CarImplWrapper implements ICar {

    private ICar car;

    public CarImplWrapper(ICar car) {
        this.car = car;
    }

    @Override
    public void start() {
        System.out.println("先檢查車");
        car.start();
        System.out.println("請注意天氣");
    }

    @Override
    public void run() {
        car.run();
    }

    @Override
    public void stop() {
        car.start();
    }
}

   public static void main(String[] args) {
        CarImpl car = new CarImpl();
        //裝飾者模式增強方法
        CarImplWrapper wrapper = new CarImplWrapper(car);

        wrapper.start();
        wrapper.run();
        wrapper.stop();

  
    }
複製程式碼

採用上述方法,的確滿足了我們的要求,在汽車啟動前進行汽車的檢查,啟動成功後提醒司機注意天氣,但是如果汽車的啟動過程設計的過多的時候,也就是我們的介面中的方法過多的時候,採用對實現類進行包裝的話那麼介面中的每個方法都要在包裝類裡面進行呼叫。為解決這個問題,我們可以採用java中的反射類裡面的Proxy類來進行解決

  • 採用Java的動態代理
public static void main(String[] args) {
      CarImpl car = new CarImpl();

       wrapper.start();
       wrapper.run();
       wrapper.stop();

        //利用反射裡面的動態代理增強方法
        ICar iCar = (ICar) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), (proxy, method, args1) -> {
            if (method.getName().equalsIgnoreCase("start")){
                System.out.println("代理增強1");
                method.invoke(car, args1);
                System.out.println("代理增強2");
                return null;
            }
            return method.invoke(car, args1);
        });
        iCar.start();
        iCar.run();
        iCar.stop();
    }
複製程式碼

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 第一個引數:因為採用反射,所以需要ClassLoader類,可以採用當前類的classloader也可以採用介面實現類的classloader,第二個引數是介面類的陣列,這裡一定要使用實現類的介面陣列,第三個陣列採用內部類形式實現即可,具體使用方式參考上述程式碼

相關文章