Java面試知識點(七十九)設計模式之代理模式(上)

一、定義

1.什麼是代理模式

  • 代理(Proxy)模式是結構型的設計模式之一,它可以爲其他對象提供一種代理(Proxy)以控制對這個對象的訪問

  • 所謂代理,是指具有與被代理的對象具有相同的接口的類,客戶端必須通過代理與被代理的目標類交互,而代理一般在交互的過程中(交互前後),進行某些特別的處理。

  • 通俗的來講代理模式就是我們生活中常見的中介或者代理人,比如我們想要買房子或者買車,自己弄太麻煩,就可以找一箇中介,幫我全部打理好;或者父母是孩子的代理人,有些事情是需要父母出面代理孩子完成。

2.爲什麼要用代理模式

  • 中介隔離作用:在某些情況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象可以在客戶類和委託對象之間起到中介的作用,其特徵是代理類和委託類實現相同的接口
  • 開閉原則,增加功能:代理類除了是客戶類和委託類的中介之外,我們還可以通過給代理類增加額外的功能來擴展委託類的功能,這樣做我們只需要修改代理類而不需要再修改委託類,符合代碼設計的開閉原則。

3.參與角色

  • 抽象主題(Subject):真實主題與代理主題的共同接口。

  • 真實主題(RealSubject):實現抽象主題,定義真實主題所要實現的業務邏輯,供代理主題調用。

  • 代理主題(Proxy):實現抽象主題,是真實主題的代理。通過真實主題的業務邏輯方法來實現抽象方法,並可以附加自己的操作。

4.應用場景

  • 需要控制對目標對象的訪問。

  • 需要對目標對象進行方法增強。如:添加日誌記錄,計算耗時等。

  • 需要延遲加載目標對象。


代理又分爲靜態代理和動態代理,我們以孩子交學費爲例

二、靜態代理

【抽象主題】

package designpatterns.proxy;

public interface IChild {
    void money();
    void school();
}

【真實主題】

package designpatterns.proxy;

public class Child implements IChild {
    @Override
    public void money() {
        System.out.println("娃娃去交錢");
    }

    @Override
    public void school() {
        System.out.println("娃娃去上學");
    }
}

【代理主題】

package designpatterns.proxy;

public class Parent implements IChild {

    private IChild child;

    public Parent(IChild child) {
        this.child = child;
    }

    @Override
    public void money() {
        System.out.println("父母去交錢");
    }

    @Override
    public void school() {
        child.school();
    }
}

【測試環境】

package designpatterns.proxy;

public class Demo {
    public static void main(String[] args) {
        IChild child = new Child();
        IChild proxy = new Parent(child);
        proxy.money();
        proxy.school();
    }
}

【運行結果】

父母去交錢
娃娃去上學

注意:在代理主題中引入的對象是抽象主題類而不是真實主題類


三、動態代理

動態代理的實現手段:JDK 自帶的 Proxy 類、CGlib、Javaassist 等。

1.Proxy類實現動態代理

抽象主題和真實主題不變

【代理處理類】

package designpatterns.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ChildHandle implements InvocationHandler {

    private IChild child;

    public ChildHandle(IChild child) {
        this.child = child;
    }

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

        /**
         *  method.getName()是獲得抽象主題的方法,通過多態定位到具體實現類
         *  當在生產環境進行調用的時候,根據不同的方法名執行不同的方法
         * */
        if ("school".equals(method.getName())) {
            obj = method.invoke(child,args);
        }

        if ("money".equals(method.getName())) {
            System.out.println("父母交學費");
        }

        return obj;
    }
}

【測試環境】

package designpatterns.proxy;

import java.lang.reflect.Proxy;

public class Dynamic {
    public static void main(String[] args) {
        IChild child = new Child();
        ChildHandle handler = new ChildHandle(child);

        // 代理模式
        IChild proxy = (IChild) Proxy.newProxyInstance(
                child.getClass().getClassLoader(),
                child.getClass().getInterfaces(),
                handler);
		// 這裏的方法調用,對用的就是ChildHandle類中invoke方法中method.getName()所得到得內容
        proxy.money();
        proxy.school();
    }
}

【運行結果】

父母交學費
娃娃去上學

注意 Proxy.newProxyInstance() 方 法接受三個參數

  • ClassLoader loader: 指定當前目標對象使用的類加載器,獲取加載器的方法是固定的
  • Class<?>[] interfaces: 指定目標對 象實現的接口的類型,使用泛型方式確認類型
  • InvocationHandler:指定動態處理器,執行目標對象的方法時,會觸發事件處理器的方法

關於proxy動態代理的個人理解:proxy動態代理的InvocationHandler 接口就類似多線程的runnable接口,而invoke方法就相當於run方法,多線程的邏輯寫在run方法中,而代理模式中需要實現的邏輯是寫在invoke方法中,而通過java反射,又可以在運行的時候獲取抽象主題和真實主題的方法,然後重新實現代理邏輯。實現的方式跟多線程的start方式略有不同,代理是通過不同的方法調用來實現的,代理的類的方式通過invocationHandle的invoke,邏輯已經更改。

總結:雖然相對於靜態代理,動態代理大大減少了我們的開發任務,同時減少了對業務接口的依賴,降低了耦合度。但是還是有一點點小小的遺憾之處,那就是Java 的繼承機制註定了這些動態代理類們無法實現對 class 的動態代理,僅支持 interface 代理。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章