代理模式

目錄

現實例子

概念

使用場景

作用和意義

1、中介隔離作用

2、開閉原則,增加功能

優缺點

優點

缺點

UML結構圖

角色構成

抽象主題(Subject)類

真實主題(Real Subject)類

代理(Proxy)類

代理模式的種類

靜態代理

定義

優點

缺點

動態代理

定義

小結

靜態代理和動態代理區別和聯繫

共同點

不同點

代碼實現

靜態代理

ILogger.java

RealLog.java

LogProxy.java

StaticProxyClient.java

靜態代理控制檯輸出

動態代理

LogUtils.java(只是工具類)

ICalculator.java

MyCalculatorImpl.java

CalculatorProxy.java

DynamicProxyClient.java

動態代理控制檯輸出

​ 


現實例子

什麼是代理模式?舉個例子來說明:假如說我現在想買一輛二手車,雖然我可以自己去找車源,做質量檢測等一系列的車輛過戶流程,但是這確實太浪費我得時間和精力了。我只是想買一輛車而已爲什麼我還要額外做這麼多事呢?於是我就通過中介公司來買車車,他們來給我找車源,幫我辦理車輛過戶流程,我只是負責選擇自己喜歡的車,然後付錢就可以了。

再比如打官司, 爲什麼要找個律師? 因爲你不想參與中間過程的是是非非, 只要完成自己的答辯就成, 其他的事前調查、事後追查等都由律師來搞定, 這就是爲了減輕你的負擔。

概念

爲其他對象提供一種代理,以控制對這個對象的訪問。代理對象在客戶端和目標對象之間起到中介的作用。

使用場景

1、限制目標對象。

2、保護目標對象。

3、增強目標對象。

作用和意義

1、中介隔離作用

在某些情況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象可以在客戶類和委託對象之間起到中介的作用,其特徵是代理類和委託類實現相同的接口。

2、開閉原則,增加功能

代理類除了是客戶類和委託類的中介之外,我們還可以通過給代理類增加額外的功能來擴展委託類的功能,這樣做我們只需要修改代理類而不需要再修改委託類,符合代碼設計的開閉原則。

代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後對返回結果的處理等。代理類本身並不真正實現服務,而是同過調用委託類的相關方法,來提供特定的服務。真正的業務功能還是由委託類來實現,但是可以在業務功能執行的前後加入一些公共的服務。例如我們想給項目加入緩存、日誌這些功能,我們就可以使用代理類來完成,而沒必要打開已經封裝好的委託類。

優缺點

優點

1、代理模式能將代理對象與真實對象被調用的目標對象分離。

2、一定程度上降低了系統的耦合度,擴展性好。

3、保護目標對象。

4、增強目標對象。

缺點

1、代理模式會造成系統設計中類的數目的增加。

2、在客戶端和目標對象增加一個代理對象,會造成請求處理速度變慢。

3、增加了系統的複雜度。

UML結構圖

角色構成

抽象主題(Subject)類

通過接口或抽象類聲明真實主題和代理對象實現的業務方法。

真實主題(Real Subject)類

實現了抽象主題中的具體業務,是代理對象所代表的真實對象,是最終要引用的對象。

代理(Proxy)類

提供了與真實主題相同的接口,其內部含有對真實主題的引用,它可以訪問、控制或擴展真實主題的功能。

代理模式的種類

按照代理創建的時期來進行分類的話:可以分爲兩種:靜態代理、動態代理。

靜態代理

定義

代理類在編譯期就生成。

優點

可以做到在符合開閉原則的情況下對目標對象進行功能擴展。

缺點

我們得爲每一個服務都得創建代理類,工作量太大,不易管理。同時接口一旦發生改變,代理類也得相應修改。

動態代理

定義

代理類在程序運行時,運用反射機制動態創建而成。

小結

雖然相對於靜態代理,動態代理大大減少了我們的開發任務,同時減少了對業務接口的依賴,降低了耦合度。但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持interface代理的桎梏。

靜態代理和動態代理區別和聯繫

共同點

兩種代理模式實現都在不改動基礎對象的前提下,對基礎對象進行訪問控制和擴展,符合開閉原則。

不同點

靜態代理在程序規模稍大時,重複性和脆弱性的缺點凸顯;動態代理(搭配泛型參數)實現了一個代理同時處理N多個基礎接口,本質上是代理類和基礎接口的解耦,一定程度上規避了靜態代理的缺點。

 從原理上講,靜態代理的代理類Class文件在編譯期生成,而動態代理的代理類Class文件在運行時生成,代理類在編譯階段並不存在,代理關係直到運行時才確定。

代碼實現

靜態代理

ILogger.java

package pattern.proxy.staticproxy;

/**
 * <一句話功能簡述>代理模式:靜態代理模式:抽象主題角色<br/>
 * <p>
 * <功能詳細描述>創建一個代理類和真實類的共同接口ILogger
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public interface ILogger {
    /**
     * 功能描述: 打印日誌<br>
     *
     * @author 劉斌
     * @date 2020/3/7 20:00
     * @see [相關類/方法](可選)
     * @since [產品/模塊版本](可選)
     */
    void printMessage();
}

RealLog.java

package pattern.proxy.staticproxy;

/**
 * <一句話功能簡述>代理模式:靜態代理模式:真實主題角色<br/>
 * <p>
 * <功能詳細描述>被代理類,現在想要在不改動被代理類代碼的前提下,實現對printMessage()方法前後進行操作
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class RealLog implements ILogger {

    public RealLog() {
    }

    @Override
    public void printMessage() {
        System.out.println("打印日誌");
    }
}

LogProxy.java

package pattern.proxy.staticproxy;

/**
 * <一句話功能簡述>代理模式:靜態代理模式:代理類角色<br/>
 * <p>
 * <功能詳細描述>被代理類,現在想要在不改動被代理類代碼的前提下,實現對printMessage()方法前後進行操作
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class LogProxy implements ILogger {

    // 定義一個被代理類的對象
    private RealLog log;

    public LogProxy() {

    }

    // 這個構造器傳入的是一個接口的對象
    public LogProxy(RealLog realLog) {
        this.log = realLog;
    }

    // 代理代理打印日誌信息
    @Override
    public void printMessage() {
        preGive();
        if (null == log) {
            log = new RealLog();
        }
        log.printMessage();
        postGive();
    }

    public void preGive() {
        System.out.println("Before:在printMessage之前進行一些操作");
    }

    public void postGive() {
        System.out.println("After:在printMessage之後進行一些操作");
    }
}

StaticProxyClient.java

package pattern.proxy.staticproxy;

/**
 * <一句話功能簡述>代理模式:靜態代理模式:客戶端調用<br/>
 * <p>
 * <功能詳細描述>現在想要在不改動被代理類代碼的前提下,實現對printMessage()方法前後進行操作
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class StaticProxyClient {
    public static void main(String[] args) {
        ILogger log = new LogProxy();
        log.printMessage();
    }
}

靜態代理控制檯輸出

 

動態代理

LogUtils.java(只是工具類)

package utils;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * <一句話功能簡述>日誌打印工具類<br/>
 * <p>
 * <功能詳細描述>
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class LogUtils {
    public static void startLog(Method method, Object... objects) {
        System.out.println("【" + method.getName() + "】執行開始了,他的參數爲【" + Arrays.asList(objects) + "】");
    }

    public static void endLog(Method method, Object... objects) {
        System.out.println("【" + method.getName() + "】執行結束了,他的結果爲【" + Arrays.asList(objects) + "】");
    }

    public static void exceptionLog(Method method, Exception e) {
        System.out.println("【" + method.getName() + "】執行出錯了,錯誤信息爲【" + e.getCause() + "】");
    }

    public static void finallyLog(Method method) {
        System.out.println("【" + method.getName() + "】最終執行結束了");
    }
}

ICalculator.java

package pattern.proxy.dynamicproxy;

/**
 * <一句話功能簡述>代理模式:動態代理模式:抽象主題角色<br/>
 * <p>
 * <功能詳細描述>創定義一個計算機接口
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public interface ICalculator {
    public int add(int num1, int mu2);

    public int sub(int num1, int mu2);

    public int mul(int num1, int mu2);

    public int div(int num1, int mu2);
}

MyCalculatorImpl.java

package pattern.proxy.dynamicproxy;

/**
 * <一句話功能簡述>代理模式:動態代理模式:真實主題角色<br/>
 * <p>
 * <功能詳細描述>被代理類
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class MyCalculatorImpl implements ICalculator {
    @Override
    public int add(int i, int j) {
        //手動爲方法添加日誌
        //LogUtils.startLog(i,j,"加法");
        int result;
        result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result;
        result = i - j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result;
        result = i * j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result;
        result = i / j;
        return result;
    }
}

CalculatorProxy.java

package pattern.proxy.dynamicproxy;

import utils.LogUtils;

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

/**
 * <一句話功能簡述>代理模式:動態代理模式:代理類角色<br/>
 * <p>
 * <功能詳細描述>被代理類,現在想要在不改動被代理類代碼的前提下,實現功能的擴展
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class CalculatorProxy {
    public static ICalculator getPoxy(ICalculator myCalcultor) {
        //爲執行Proxy.newProxyInstance(loader, interfaces, h),三個參數賦值
        ClassLoader classLoader = myCalcultor.getClass().getClassLoader();
        Class<?>[] interfaces = myCalcultor.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //對原方法添加日誌
                Object result = null;
                try {
                    LogUtils.startLog(method, args);
                    result = method.invoke(myCalcultor, args);
                    LogUtils.endLog(method, result);
                } catch (Exception e) {
                    LogUtils.exceptionLog(method, e);
                } finally {
                    LogUtils.finallyLog(method);
                }
                return result;
            }
        };
        //生成代理對象
        ICalculator proxy = (ICalculator) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxy;
    }
}

DynamicProxyClient.java

package pattern.proxy.dynamicproxy;

/**
 * <一句話功能簡述>代理模式:動態代理模式:客戶端調用<br/>
 * <p>
 * <功能詳細描述>
 *
 * @author 劉斌
 * @date 2020/3/7
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class DynamicProxyClient {
    public static void main(String[] args) {
        originCalc();
    }

    public static void originCalc() {
        ICalculator myCalculator = new MyCalculatorImpl();
        //int result=myCalculator.add(2, 4);

        //採用動態代理爲方法添加日誌
        ICalculator proxy = CalculatorProxy.getPoxy(myCalculator);
        proxy.add(2, 4);
        proxy.div(2, 1);
    }
}

動態代理控制檯輸出

 

 

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