每天學點設計模式---代理模式

代理模式的定義

爲其他對象提供一種代理,以控制對這個對象的訪問;
代理對象起到中介的作用,可去掉功能服務或者增加額外的服務;
比如:買火車票,可以去火車票代售處購買而不用非得去到火車站,火車票代售處就是火車站的代理,
火車票代售處和火車站的功能的比較:

功能服務 火車站 火車票代售處
火車票的銷售功能
提供額外的服務(電話預約) 沒有
提供額外的服務(收取手續費) 沒有
提供退票服務 沒有

火車票代售處就是火車站的代理,可去掉功能服務或者增加額外的服務;

代理模式兩種靜態代理和動態代理

靜態代理

靜態代理概念和模型
比如一輛汽車有行駛功能,現在要代理一下汽車行駛,額外增加時間記錄功能;
1,相同接口或抽象類

public interface Moveable {

    void move();//公共的行駛

}

//原始的小汽車 實現了Moveable

public class Car implements Moveable {

    @Override
    public void move() {
        System.out.println("汽車行駛過程中。。。。。");
    }

}
通過繼承來實現代理

現在的代理小汽車實現了 Moveable (Car本身也是實現了Moveable)

//通過繼承的方式來實現代理
public class CarProxy1 extends Car {
    
    @Override
    public void move() {
        System.out.println("汽車開始時間。。。。。");//額外增加時間統計
        super.move();
        System.out.println("汽車結束時間。。。。。");
    }
}
通過聚合來實現代理

現在的代理小汽車實現了 Moveable

public class CarProxy2 implements Moveable {

    public Moveable m;

    public CarProxy2(Car m) {
        this.m = m;
    }

    @Override
    public void move() {

        System.out.println("汽車開始時間。。。。。");//額外增加時間統計
        m.move();
        System.out.println("汽車結束時間。。。。。");

    }

}

繼承的方式相比於聚合來講不靈活;
現在我們添加一個代理日誌打印的功能;使用繼承的方式會增加下面四中代理類,每個代理類的都是特定的功能如下;
1,統計時間的代理類;
2,統計日誌的代理類;
3,先統計時間後統計日誌的代理類;
4,先統計日誌後統計時間的代理類;
如果使用組合的話,只需要兩個類就可以了;看代碼
1,統計時間代理類

//代理時間打印的代理
public class CarTimeProxy implements Moveable {

    public Moveable m;

    public CarTimeProxy(Moveable m) {
        this.m = m;
    }

    @Override
    public void move() {

        System.out.println("汽車開始時間。。。。。");//額外增加時間統計
        m.move();
        System.out.println("汽車結束時間。。。。。");

    }

}

統計時間代理類

        Car car = new Car();
        CarTimeProxy carTimeProxy = new CarTimeProxy(car);
        carTimeProxy.move();

2,統計日誌代理類

//代理日誌打印功能的代理
public class CarLogProxy implements Moveable {

    public Moveable m;

    public CarLogProxy(Moveable m) {
        this.m = m;
    }

    @Override
    public void move() {

        System.out.println("汽車開始日誌。。。。。");//額外增加日誌打印統計
        m.move();
        System.out.println("汽車結束日誌。。。。。");

    }

}

統計日誌打印代理類

        Car car = new Car();
        CarLogProxy carLogProxy = new CarLogProxy(car);
        carLogProxy.move();

3,先統計時間後統計日誌的代理

        Car car = new Car();
        CarTimeProxy carTimeProxy = new CarTimeProxy(car);
        CarLogProxy carLogProxy = new CarLogProxy(carTimeProxy);
        carLogProxy.move();

4,先統計日誌後統計時間的代理

        Car car = new Car();
        CarLogProxy carLogProxy = new CarLogProxy(car);
        CarTimeProxy carTimeProxy = new CarTimeProxy(carLogProxy);
        carTimeProxy.move();

從上面看使用組合方式只需要使用2個類就ok了;

動態代理

上面的小汽車需要做時間上的代理,那麼自行車,火車,也需要時間上的代理,其它失誤也需要時間上的代理,如下圖所示,有一百個事物就需要做100個事物對象的時間代理;
時間代理
那樣會造成代理類膨脹;那麼有沒有一種方式 實現對不同類,不同方法的代理這產生裏動態代理,
上面的事例中不同的想要代理時間的對象都是動態產生,這就屬於動態代理;

動態代理就是在代理對象和被代理對象之間添加了一個事物處理器;如下圖代理的關鍵兩個類:
Proxy:
InVocationHandler:
動態代理的概念和模型
動態代理
具體代碼實現:

        //這個Car類,現在要對它做一個時間代理(使用動態代理)
        final Car car = new Car();
        //實現一個InvocationHandler接口
        InvocationHandler invocationHandler = new InvocationHandler() {

            /**
             * @param proxy  代理類Proxy,它是JDK內部的類
             * @param method 被代理類的方法
             * @param args   被代理類方法的參數
             * @return 被代理類的方法返回值
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("汽車行駛開始時間。。。。。");
                method.invoke(car, args);
                System.out.println("汽車行駛結束時間。。。。。");
                return null;
            }
        };

        ClassLoader classLoader = Car.class.getClassLoader();
        Class<?>[] interfaces = Car.class.getInterfaces();
        //產生一個代理對象
        final Moveable m = (Moveable) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        //調用方法,就會調用InvocationHandler的invoke方法,整個代理過程都在這個方法裏面執行
        m.move();

總結動態代理:這個鬼東西真的很玄幻啊,我剛開始是很暈暈的,
使用一個僞代碼解釋一下,

public class CarProxy implements Moveable{
    
    InvocationHandler invocationHandler;
    @Override
    public void move() {
        invocationHandler.invoke()
    }
    
}

1,Proxy.newProxyInstance生成的代理類class是在運行時生成,
Proxy.newProxyInstance()生成的就是一個類似上面的代理類CarProxy,裏面含有InvocationHandler屬性

2,Proxy.newProxyInstance生成的代理類class需要實現一組接口
這裏這個代理類CarProxy也是實現了Moveable接口

3,必須實現InvocationHandler,具體代理類業務是在invoke方法裏面實現;

當代理類CarProxy調用自己的方法比如move()時候,相當於這個時候InvocationHandler調用自己的invoke方法來執行的;

發佈了92 篇原創文章 · 獲贊 68 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章