設計模式(四)——搞懂什麼是代理模式

代理模式

定義:爲其餘對象提供一種代理以控制對這個對象的訪問java

代理模式的通用類圖

上圖中,Subject是一個抽象類或者接口,RealSubject是實現方法類,具體的業務執行,Proxy則是RealSubject的代理,直接和client接觸的。git

代理模式能夠在不修改被代理對象的基礎上,經過擴展代理類,進行一些功能的附加與加強。值得注意的是,代理類和被代理類應該共同實現一個接口,或者是共同繼承某個類。github

代理模式優勢

  • 職責清晰
  • 高擴展,只要實現了接口,均可以用代理。
  • 智能化,動態代理。

分類

代碼:GitHubshell

一、靜態代理

以租房爲例,咱們通常用租房軟件、找中介或者找房東。這裏的中介就是代理者。編程

首先定義一個提供了租房方法的接口。微信

public interface IRentHose {
    void rentHose();
}
複製代碼

定義租房的實現類ide

public class RentHose implements IRentHose {
    @Override
    public void rentHose() {
        System.out.println("租了一間房子。。。");
    }
}
複製代碼

我要租房,房源都在中介手中,因此找中介測試

public class IntermediaryProxy implements IRentHose {

    private IRentHose rentHose;

    public IntermediaryProxy(IRentHose irentHose){
        rentHose = irentHose;
    }

    @Override
    public void rentHose() {
        System.out.println("交中介費");
        rentHose.rentHose();
        System.out.println("中介負責維修管理");
    }
}
複製代碼

這裏中介也實現了租房的接口。this

再main方法中測試spa

public class Main {

    public static void main(String[] args){
        //定義租房
        IRentHose rentHose = new RentHose();
        //定義中介
        IRentHose intermediary = new IntermediaryProxy(rentHose);
        //中介租房
        intermediary.rentHose();
    }
}
複製代碼

返回信息

交中介費
租了一間房子。。。
中介負責維修管理
複製代碼

這就是靜態代理,由於中介這個代理類已經事先寫好了,只負責代理租房業務

二、強制代理

若是咱們直接找房東要租房,房東會說我把房子委託給中介了,你找中介去租吧。這樣咱們就又要交一部分中介費了,真坑。

來看代碼如何實現,定義一個租房接口,增長一個方法。

public interface IRentHose {
    void rentHose();
    IRentHose getProxy();
}
複製代碼

這時中介的方法也稍微作一下修改

public class IntermediaryProxy implements IRentHose {

    private IRentHose rentHose;

    public IntermediaryProxy(IRentHose irentHose){
        rentHose = irentHose;
    }

    @Override
    public void rentHose() {
        rentHose.rentHose();
    }

    @Override
    public IRentHose getProxy() {
        return this;
    }
}
複製代碼

其中的getProxy()方法返回中介的代理類對象

咱們再來看房東是如何實現租房:

public class LandLord implements IRentHose {

    private IRentHose iRentHose = null;

    @Override
    public void rentHose() {
        if (isProxy()){
            System.out.println("租了一間房子。。。");
        }else {
            System.out.println("請找中介");
        }
    }

    @Override
    public IRentHose getProxy() {
        iRentHose = new IntermediaryProxy(this);
        return iRentHose;
    }

    /** * 校驗是不是代理訪問 * @return */
    private boolean isProxy(){
        if(this.iRentHose == null){
            return false;
        }else{
            return true;
        }
    }
}
複製代碼

房東的getProxy方法返回的是代理類,而後判斷租房方法的調用者是不是中介,不是中介就不租房。

main方法測試:

public static void main(String[] args){

        IRentHose iRentHose = new LandLord();
        //租客找房東租房
        iRentHose.rentHose();
        //找中介租房
        IRentHose rentHose = iRentHose.getProxy();
        rentHose.rentHose();


    }

}
複製代碼
請找中介
租了一間房子。。。
複製代碼

看,這樣就是強制你使用代理,若是不是代理就無法訪問。

三、動態代理

咱們知道如今的中介不單單是有租房業務,同時還有賣房、家政、維修等得業務,只是咱們就不能對每個業務都增長一個代理,就要提供通用的代理方法,這就要經過動態代理來實現了。

中介的代理方法作了一下修改

public class IntermediaryProxy implements InvocationHandler {


    private Object obj;

    public IntermediaryProxy(Object object){
        obj = object;
    }

    /** * 調用被代理的方法 * @param proxy * @param method * @param args * @return * @throws Throwable */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj, args);
        return result;
    }

}
複製代碼

在這裏實現InvocationHandler接口,此接口是JDK提供的動態代理接口,對被代理的方法提供代理。其中invoke方法是接口InvocationHandler定義必須實現的, 它完成對真實方法的調用。動態代理是根據被代理的接口生成全部的方法,也就是說給定一個接口,動態代理就會實現接口下全部的方法。經過 InvocationHandler接口, 全部方法都由該Handler來進行處理, 即全部被代理的方法都由 InvocationHandler接管實際的處理任務。

這裏增長一個賣房的業務,代碼和租房代碼相似。

main方法測試:

public static void main(String[] args){

    IRentHose rentHose = new RentHose();
    //定義一個handler
    InvocationHandler handler = new IntermediaryProxy(rentHose);
    //得到類的class loader
    ClassLoader cl = rentHose.getClass().getClassLoader();
    //動態產生一個代理者
    IRentHose proxy = (IRentHose) Proxy.newProxyInstance(cl, new Class[]{IRentHose.class}, handler);
    proxy.rentHose();

    ISellHose sellHose = new SellHose();
    InvocationHandler handler1 = new IntermediaryProxy(sellHose);
    ClassLoader classLoader = sellHose.getClass().getClassLoader();
    ISellHose proxy1 = (ISellHose) Proxy.newProxyInstance(classLoader, new Class[]{ISellHose.class}, handler1);
    proxy1.sellHose();

}
複製代碼
租了一間房子。。。
買了一間房子。。。
複製代碼

在main方法中咱們用到了Proxy這個類的方法,

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 複製代碼

loder:類加載器,interfaces:代碼要用來代理的接口, h:一個 InvocationHandler 對象 。

InvocationHandler 是一個接口,每一個代理的實例都有一個與之關聯的 InvocationHandler 實現類,若是代理的方法被調用,那麼代理便會通知和轉發給內部的 InvocationHandler 實現類,由它決定處理。

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
複製代碼

InvocationHandler 內部只是一個 invoke() 方法,正是這個方法決定了怎麼樣處理代理傳遞過來的方法調用。

由於,Proxy 動態產生的代理會調用 InvocationHandler 實現類,因此 InvocationHandler 是實際執行者。

總結

  1. 靜態代理,代理類須要本身編寫代碼寫成。
  2. 動態代理,代理類經過 Proxy.newInstance() 方法生成。
  3. JDK實現的代理中無論是靜態代理仍是動態代理,代理與被代理者都要實現兩樣接口,它們的實質是面向接口編程。CGLib能夠不須要接口。
  4. 動態代理經過 Proxy 動態生成 proxy class,可是它也指定了一個 InvocationHandler 的實現類。

歡迎關注:

公衆號微信
相關文章
相關標籤/搜索