1. 程式人生 > 其它 >java 設計模式之代理模式(七)

java 設計模式之代理模式(七)

技術標籤:Android設計模式java代理模式

java 設計模式之代理模式⑦

不要隨意發脾氣,人生在世,一個人不可能和所有認識的人都成為朋友。真正的朋友是強求不了的,老天也沒規定誰必須和誰成為朋友,凡事都是要隨緣的,生活中什麼事都可以勉強,惟有“情”勉強不得,友情也是一樣的道理。

設計模式學習,近期我會把23中設計模式都寫成部落格,敬請期待~
—2021/1/9

定義

為其他物件提供一種代理以控制對這個物件的訪問。在某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。

百度百科

角色分類

  • 抽象角色:通過介面或抽象類宣告真實角色實現的業務方法。
  • 代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。
  • 真實角色:實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色呼叫

分析

假設我要去租房子,我肯定是先去找中介看房子,然後選取合適的房子租;

房子那裡來的?那肯定是房東的吧

在這裡房東就是一個需要代理的人,代理的東西就是房子,吧房子代理給中介,讓中介幫忙出租給我們.

好處:

  • 房東沒有任何改變,只是吧房子代理給了中介,中介得到房子之後向我們出租,並收取中介費等等.

UML類圖(1.1):


分析:

  • HouseMaster 真實角色
    這裡指房東
  • IHouse 抽象角色這裡指房子
  • HouseProxy 代理角色這裡指中介

靜態代理實現:

IHouse(房子):

public interface IHouse {
    void showHouse();
}

HouseMaster(房東):

public class HouseMaster implements IHouse {

    @Override
    public void showHouse() {
        Log.i("代理模式:", "我是房東,我要出租房");
    }
}

HouseProxy(中介):

public class HouseProxy {

    IHouse house;
    //吧房子交給中介 (吧IHouse 介面組合到 HouseProxy 上)
    public HouseProxy( IHouse house) {
        this.house = house;
    }

    public void showHouse(){
        house.showHouse();//房東的房子
    }

}

使用程式碼:

//建立代理類 傳入需要代理的類
HouseProxy houseProxy = new HouseProxy(new HouseMaster());

houseProxy.showHouse();

Log圖(2.1):


可能大家對代理模式還是有所迷,我在詳細解釋一下

現在咋們只是通過HouseProxy(中介)來看房東A的房子,他還可以做其他操作,比如說,我們可以讓他帶著我們去看看其他的房子,我們還可以給中介小費:

HouseProxy(中介)類:

public class HouseProxy {

    IHouse house;
    public HouseProxy( IHouse house) {
        this.house = house;
    }

    public void showHouse(){
        money();//收取消費
        house.showHouse();//房東的房子
        seeHouse();//帶使用者看房
    }
    public void money(){
        Log.i("代理模式:","我是中介,我要收取消費");
    }
    public void seeHouse(){
        Log.i("代理模式:","我是中介,我帶使用者看房");
    }
}

Log圖(2.2):


房東並沒有發生變化,變化的只是中介
比如說在專案中,一個類用到了很多地方現在讓這個這個類前面輸出一句話,
咋們是不是就可以不在改變原有程式碼的基礎上來達到這句話的顯示呢?

靜態代理模式總結:

優點:

  • 沒有改變原有的程式碼,滿足開閉原則(對修改關閉)
  • 公共程式碼交給代理類,實現了業務的分工
  • 不用關心其他非本職責的事務,通過後期的代理完成一件完成事務,附帶的結果就是程式設計簡潔清晰。

缺點:

  • 一個類就需要建立一個代理類,程式碼量會翻倍,開發效率會降低

動態代理模式

動態代理指:代理類可以隨著被代理的變化而變化,

簡單的說就是我想A被代理,就A代理,想讓B代理就被B代理,不用建立很多個代理類

角色:

  • InvocationHandler 呼叫處理程式
  • Proxy 代理

InvocationHandler是什麼?

JDK1.8CHW圖(3.1):


JDK1.8CHW下載 提取碼:lfoz

InvocationHandler是由代理例項的呼叫處理程式實現的介面


InvocationHandler是一個介面,需要重寫
invoke(Object proxy, Method method, Object[] args)方法

invoke方法解釋:

  • 引數一(Object proxy):呼叫該方法的代理例項
  • 引數二(Method method):方法物件的宣告類將是該方法宣告的介面,它可以是代理類繼承該方法的代理介面的超級介面。 (這裡聽不懂沒關係)
  • 引數三(Object[] args):包含的方法呼叫傳遞代理例項的引數值的物件的陣列,或null如果介面方法沒有引數。 原始型別的引數包含在適當的原始包裝器類的例項中,例如java.lang.Integer或java.lang.Boolean 。

這是jdk裡面的解釋:這裡的方法解釋看不明白沒關係.

JDK1.8CHW圖(3.2):

Proxy是什麼?

JDK1.8CHW圖(3.3):


Proxy提供了建立動態代理類和例項的靜態方法,它也是由這些方法建立的所有動態代理類的超類。 (官方話)

我的理解:Proxy就是為了獲取代理的例項

這麼多字大家肯定不想看,簡單的說就是:Proxy配合InvocationHandler使用完成代理類的自動生成

如果獲取被代理的例項:

Proxy.newProxyInstance(
		ClassLoader loader,
        Class<?>[] interfaces,
        InvocationHandler h);

Proxy.newProxyInstance引數分析:

  • 引數一: 載入類所在位置
  • 引數二: 代理的介面
  • 引數三: 表示本身:InvocationHandler

程式碼實現:

ProxyInvocationHandler類:

public class ProxyInvocationHandler implements InvocationHandler {

    Object object;

    //引數一:object 具體介面的實現類
    public ProxyInvocationHandler(Object object) {
        this.object = object;
    }

    //生成代理類  返回的是被代理的介面 (返回的是object介面)
    public Object getProxy(){
        /**
         * this.getClass().getClassLoader() 載入類所在位置
         * object.getClass().getInterfaces() 代理的介面
         * this  表示本身:InvocationHandler
         */
       return Proxy.newProxyInstance(
			       this.getClass().getClassLoader(),
			       object.getClass().getInterfaces(),
			       this
	     	 );
    }

    /**
     * @param proxy 呼叫該方法的代理例項
     * @param method  所述方法對應於呼叫代理例項上的介面方法的例項。 方法物件的宣告類將是該方法宣告的介面,它可以是代理類繼承該方法的代理介面的超級介面。
     * @param args 包含的方法呼叫傳遞代理例項的引數值的物件的陣列,或null如果介面方法沒有引數。
     *             原始型別的引數包含在適當的原始包裝器類的例項中,例如java.lang.Integer或java.lang.Boolean 。
     * @return
     * @throws Throwable
     */
    @Override  //處理代理例項,返回結果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(object, args);
        return  invoke;
    }
}

使用:

//需要代理 真實角色
HouseMaster houseMaster = new HouseMaster();

//代理類生成器  傳入需要代理的類
ProxyInvocationHandler pit =
			 new ProxyInvocationHandler(houseMaster);

//生成代理類 (必須返回介面)
IHouse proxy = (IHouse) pit.getProxy();

//輸出租房子
proxy.showHouse();

Log圖(2.3):


HouseMaster(代理類)通過ProxyInvocationHandler(呼叫處理程式)類的反射機制自動生成了對應的IHouse(介面)

注意:


利用InvocationHandler的反射機制,獲取當前呼叫的方法:

public class ProxyInvocationHandler implements InvocationHandler {

	.....
	
    @Override  //處理代理例項,返回結果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        msg(method.getName());
        Object invoke = method.invoke(object, args);
        return  invoke;
    }

    public void msg(String msg){
        Log.i("代理模式","代理了"+ msg +"方法");
    }
}

流程圖(4.1):


這裡就是獲取的showHouse()方法名

Log圖(2.4):

現在這個ProxyInvocationHandler就是一個公共的代理類,傳遞進去一個需要代理的類,然後通過getProxy()就可以返回一個代理類的物件,(前提是傳遞進去的代理類要實現返回的代理類哦)

優點:

  • 沒有改變原有的程式碼,滿足開閉原則(對修改關閉)
  • 公共程式碼交給代理類,實現了業務的分工
  • 動態代理代理的是一個介面,一般是對應的一個業務
  • 一個動態代理可以代理多個介面,只要是實現同一個介面即可,相對於靜態代理每一個被代理類都需要建立一個代理類來說,更加靈活

如果實在看不懂本篇的話會用也可以,因為這動態程式碼比較固定!

只要知道每個類扮演對應的角色會用就可以.

  • ProxyInvocationHandler呼叫處理程式 (用來自動生成代理類)

  • HouseMaster 真實角色(需要被代理的(本篇指房東出租房子))

  • IHouse 抽象角色(這裡指房子)

  • pit.getProxy() 返回抽象角色(這裡指返回房子)

  • proxy.showHouse(); 具體實現(指租房子)

完整專案

去設計模式/設計原則主頁

原創不易,您的點贊就是對我最大的支援,點個贊支援一下哦~