設(shè)計(jì)模式-代理模式

今天想閱讀Retrofit的源碼,發(fā)現(xiàn)一開(kāi)始就是一個(gè)代理。對(duì)代理的模式又不是很了解。學(xué)習(xí)了一波。

什么是代理

我們買(mǎi)衣服有代理商,存在于我們和廠家之間。廠家是賣(mài)衣服的,代理商也是賣(mài)衣服的,我們?nèi)ベI(mǎi)衣服,一般不
能直接找廠家,所以我們找代理商買(mǎi)衣服,代理商找廠家買(mǎi)衣服,代理商進(jìn)行提價(jià),就是我們需要
讓代理,進(jìn)行的操作。

代理的理解

代理模式需要3個(gè)角色。

  1. 抽象角色 聲明真實(shí)對(duì)象和代理對(duì)象共有的操作
  2. 代理對(duì)象 代理對(duì)象持有真實(shí)對(duì)象的引用,從而可以對(duì)真實(shí)對(duì)象進(jìn)行操作,同時(shí)代理對(duì)象和真實(shí)對(duì)象實(shí)現(xiàn)相同的接口,可以在任何時(shí)刻替換掉真實(shí)對(duì)象。
    代理對(duì)象調(diào)用真實(shí)對(duì)象的操作是,可以加上自己的操作,相當(dāng)于對(duì)真實(shí)對(duì)象進(jìn)行封裝。
  3. 真實(shí)對(duì)象 代理對(duì)象代表的對(duì)象,是我們最終引用的對(duì)象。

代理模式對(duì)外部提供統(tǒng)一的接口方法,,而代理類(lèi)在接口中實(shí)現(xiàn)對(duì)真實(shí)類(lèi)的附加操作行為,從而可以在不影響外部調(diào)用情況下,進(jìn)行系統(tǒng)擴(kuò)展。也就是說(shuō),我要修改真實(shí)角色的操作的時(shí)候,盡量不要修改他,而是在外部在“包”一層進(jìn)行附加行為,即代理類(lèi)。
從而達(dá)到對(duì)修改關(guān)閉,對(duì)擴(kuò)展開(kāi)放,保證系統(tǒng)的穩(wěn)定性。
與真實(shí)對(duì)象不存在聯(lián)系,降低耦合

靜態(tài)代理

在編碼過(guò)程中對(duì)代理進(jìn)行指定。和生活中的代理商一樣。
抽象對(duì)象 賣(mài)衣服

public interface ClothingSell {
    void sellClothes();
}

代理對(duì)象 代理商

package staticProxy;

/**
 * Created by azezer0 on 16/10/21.
 */
public class ClothingFactor implements ClothingSell {
    private ClothingFactory clothingFactory;

    public ClothingFactor(ClothingFactory clothingFactory) {
        this.clothingFactory = clothingFactory;
    }

    public ClothingFactor() {
        this.clothingFactory = new ClothingFactory();
    }


    @Override
    public void sellClothes() {
        System.out.println("我是一個(gè)買(mǎi)衣服的代理商,先漲價(jià)3倍");
        clothingFactory.sellClothes();
        System.out.println("衣服賣(mài)好了,再見(jiàn)");
    }
}

真實(shí)對(duì)象

package staticProxy;

/**
 * Created by azezer0 on 16/10/21.
 */
public class ClothingFactory implements ClothingSell {


    @Override
    public void sellClothes() {
        System.out.println("我是一個(gè)衣服廠家");
    }
}

執(zhí)行

package staticProxy;

/**
 * Created by azezer0 on 16/10/21.
 */
public class Main {
    public static void main(String str[])
    {
        ClothingFactor clothingFactor = new ClothingFactor();
        System.out.println("我要買(mǎi)衣服");
        clothingFactor.sellClothes();
        System.out.println("衣服買(mǎi)好了");
    }
}

結(jié)果

我要買(mǎi)衣服
我是一個(gè)買(mǎi)衣服的代理商,先漲價(jià)3倍
我是一個(gè)衣服廠家
衣服賣(mài)好了,再見(jiàn)
衣服買(mǎi)好了

動(dòng)態(tài)代理

在程序運(yùn)行過(guò)程中,才生出代理類(lèi),并執(zhí)行.由于是在執(zhí)行過(guò)程中,才會(huì)身材代理類(lèi)。如果大規(guī)模采用靜態(tài)代理,產(chǎn)生大量的類(lèi)同時(shí)代理角色與真實(shí)角色功能重復(fù),是系統(tǒng)臃腫復(fù)制。

代理原理

java編譯器編譯完成以后,生成class文件,虛擬機(jī)讀取字節(jié)碼文件,加載內(nèi)存,解析,生成Class對(duì)象。而動(dòng)態(tài)代理就是在程序運(yùn)行過(guò)程中直接生成新的class字節(jié)碼二進(jìn)制數(shù)據(jù),由jvm加載執(zhí)行,從而完成動(dòng)態(tài)代理的操作。
但是在動(dòng)態(tài)生成代碼的過(guò)程,如果完成一行一行的實(shí)現(xiàn)一個(gè)新的類(lèi),是十分繁瑣而且容易出錯(cuò),我們使用代理模式是系統(tǒng)在不影響系統(tǒng)穩(wěn)定是的前提下,對(duì)系統(tǒng)進(jìn)行擴(kuò)展。
代理模式中,Proxy角色,在執(zhí)行代理操作時(shí),也就是在執(zhí)行真實(shí)對(duì)象的操作之前和之后執(zhí)行了”新添加的邏輯“,所以我們完全可以這一部分抽象出來(lái)。這就有了InvocationHandler
動(dòng)態(tài)代理架構(gòu)

JDK動(dòng)態(tài)代理創(chuàng)建機(jī)制

代理類(lèi)有java動(dòng)態(tài)生成,所以將會(huì)執(zhí)行以下操作

  1. 獲取真實(shí)對(duì)象的接口
  2. 生成代理名稱(chēng)
  3. 動(dòng)態(tài)創(chuàng)建代理對(duì)象的字節(jié)碼
  4. 創(chuàng)建InvocationHanlder的實(shí)例Handler,出來(lái)代理對(duì)象的方法調(diào)用。
  5. 實(shí)例化代理對(duì)象。

場(chǎng)景

電動(dòng)汽車(chē)的特點(diǎn),能開(kāi),能充電。
真實(shí)對(duì)象 電動(dòng)車(chē)
代理獨(dú)享 (動(dòng)態(tài)生成)
抽象對(duì)象 駕駛接口 充電接口
InVocationHandler handler

電動(dòng)車(chē)

package proxyTest;

/**
 * Created by azezer0 on 16/10/21.
 */
public class ElectricCar implements Rechargable, Vehicle {
    public static final String Tag = ElectricCar.class.getSimpleName();

    @Override
    public void recharge() {
        System.out.println(Tag+"__"+"recharge");
    }

    @Override
    public void driver() {
        System.out.println(Tag + "__" + "driver");
    }
}

駕駛接口

package proxyTest;

/**
 * Created by azezer0 on 16/10/21.
 */
public interface Vehicle {
    void driver();
}

充電接口

package proxyTest;

/**
 * Created by azezer0 on 16/10/21.
 */
public interface Rechargable {

    void recharge();
}

handler 實(shí)現(xiàn) InvocationHandler接口

package proxyTest;

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

/**
 * Created by azezer0 on 16/10/21.
 */
public class InvocationHandlerImpl implements InvocationHandler {
    private ElectricCar car;

    public InvocationHandlerImpl(ElectricCar car) {
        this.car = car;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Handler" + "___" + method.getName() + "___Start");
        method.invoke(car, null);
        System.out.println("Handler" + "___" + method.getName() + "___Stop");
        return null;
    }
}

生成代理類(lèi),并執(zhí)行

package proxyTest;

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

/**
 * Created by azezer0 on 16/10/21.
 */
public class TestMain {
    public static void main(String args[]) {
        //電動(dòng)車(chē)實(shí)體類(lèi)
        ElectricCar car = new ElectricCar();
        //獲取車(chē)的類(lèi)加載器
        ClassLoader classLoader = car.getClass().getClassLoader();
        Class[] interfacess = car.getClass().getInterfaces();
        InvocationHandler handler = new InvocationHandlerImpl(car);
        //拿到代理類(lèi)
        Object o = Proxy.newProxyInstance(classLoader, interfacess, handler);
        //執(zhí)行操作
        Vehicle vehicle = (Vehicle) o;
        vehicle.driver();
        Rechargable rechargable = (Rechargable) o;
        rechargable.recharge();
        //保存到本地 class 文件
        // ProxyUtils.generateClassFile(car.getClass(),"ElectirCarProxy");
    }
}

執(zhí)行結(jié)果

Handler___driver___Start
ElectricCar__driver
Handler___driver___Stop
Handler___recharge___Start
ElectricCar__recharge
Handler___recharge___Stop
/Users/azezer0/x603/study/HTTPS/out/production/HTTPS/proxyTest/

保存生成的字節(jié)碼工具到本地

package proxyTest;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Created by azezer0 on 16/10/21.
 */
public class ProxyUtils {
    public static void generateClassFile(Class clazz, String proxyName) {
        byte[] bytes = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(bytes);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

總結(jié)

程序只是對(duì)生活的抽象,又一次真實(shí)的感受到了,設(shè)計(jì)模式是對(duì)程序的抽象,所以是所有的設(shè)計(jì)模式,都是可以在生活中找到實(shí)例的。

1.遠(yuǎn)程代理 Remote Proxy
通過(guò)代理來(lái)表示另一個(gè)地址空間中的對(duì)象
2.虛代理 Virtual Proxy
按需創(chuàng)建開(kāi)銷(xiāo)很大的對(duì)象. 常用于延遲加載. 
3.保護(hù)代理 Protection Proxy
-控制對(duì)原始對(duì)象的訪問(wèn). 
-在訪問(wèn)對(duì)象時(shí)可以附加一些操作
4.智能指引 Smart Reference
-充當(dāng)智能指針: 能自動(dòng)釋放所引用的對(duì)象
-在訪問(wèn)對(duì)象時(shí)可以附加一些操作

在面向?qū)ο笾蟹Q(chēng)之為: 職責(zé)分明。
很不錯(cuò)的解釋

打個(gè)比喻,明星為什么需要經(jīng)紀(jì)人來(lái)代理他呢?因?yàn)槊餍堑膶?zhuān)職是唱歌或演戲,如果把除此以外的其他事情比如演出費(fèi)用談判等等都攬?jiān)谏砩?,他?huì)累死。

這就是體現(xiàn)一個(gè)思想:專(zhuān)業(yè)分工,用面向?qū)ο笮g(shù)語(yǔ)說(shuō):就是職責(zé)分明。

所以,代理類(lèi)一般是做些除原始類(lèi)核心功能以外的其他功能,比如權(quán)限 事務(wù)等等都要專(zhuān)門(mén)的代理來(lái)實(shí)現(xiàn)。

當(dāng)我們的代碼每個(gè)類(lèi)代表一個(gè)主要功能,而不是將所有功能混在一個(gè)類(lèi)中,那么代碼無(wú)疑清晰有條理的,易于維護(hù),比如我要修改權(quán)限,就不必打開(kāi)原始類(lèi)代碼,直接修改權(quán)限代理類(lèi)就可以了。就很少發(fā)生修改一個(gè)BUG,帶來(lái)一個(gè)新BUG的煩惱問(wèn)題。

No matter how slow you are writing clean code, you will always be slower if you make a mess 寫(xiě)干凈的代碼無(wú)論有多慢,總是快于把代碼寫(xiě)得一團(tuán)糟。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、概述 ??代理模式我們接觸的就比較多了,所謂的代理模式就是,給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)原...
    騎著烏龜去看海閱讀 1,016評(píng)論 0 9
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是代理模式 模式的結(jié)構(gòu) 典型代碼 代理模式分類(lèi) 代碼示例 代理模式和裝飾者模式的...
    w1992wishes閱讀 1,647評(píng)論 0 13
  • 1. 定義 為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。 2. 使用場(chǎng)景 當(dāng)想對(duì)某個(gè)對(duì)象做功能增強(qiáng)拓展,但又不想...
    程序員修仙閱讀 446評(píng)論 1 2
  • 前兩天有人問(wèn)我 你有沒(méi)有特別喜歡的人 或者傷你特別深現(xiàn)在想起來(lái)都會(huì)難過(guò)得人 我說(shuō)有 現(xiàn)在想起來(lái)不會(huì)難過(guò) 我們沒(méi)在一...
    96d6e44e065d閱讀 386評(píng)論 0 2
  • #本文參加‘青春’大賽,本人保證本文為本人原創(chuàng),如有問(wèn)題則與主辦方無(wú)關(guān),自愿放棄評(píng)優(yōu)評(píng)獎(jiǎng)資格 姓名:陳金惠 學(xué)校:...
    蠟淚閱讀 290評(píng)論 0 0

友情鏈接更多精彩內(nèi)容