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

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

什么是代理

我們買衣服有代理商,存在于我們和廠家之間藏杖。廠家是賣衣服的将塑,代理商也是賣衣服的,我們?nèi)ベI衣服蝌麸,一般不
能直接找廠家点寥,所以我們找代理商買衣服,代理商找廠家買衣服来吩,代理商進(jìn)行提價(jià)敢辩,就是我們需要
讓代理,進(jìn)行的操作弟疆。

代理的理解

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

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

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

靜態(tài)代理

在編碼過程中對代理進(jìn)行指定。和生活中的代理商一樣娜谊。
抽象對象 賣衣服

public interface ClothingSell {
    void sellClothes();
}

代理對象 代理商

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è)買衣服的代理商,先漲價(jià)3倍");
        clothingFactory.sellClothes();
        System.out.println("衣服賣好了,再見");
    }
}

真實(shí)對象

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("我要買衣服");
        clothingFactor.sellClothes();
        System.out.println("衣服買好了");
    }
}

結(jié)果

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

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

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

代理原理

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

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

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

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

場景

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

電動(dòng)車

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;
    }
}

生成代理類本谜,并執(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)車實(shí)體類
        ElectricCar car = new ElectricCar();
        //獲取車的類加載器
        ClassLoader classLoader = car.getClass().getClassLoader();
        Class[] interfacess = car.getClass().getInterfaces();
        InvocationHandler handler = new InvocationHandlerImpl(car);
        //拿到代理類
        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é)

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

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

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

打個(gè)比喻掖肋,明星為什么需要經(jīng)紀(jì)人來代理他呢?因?yàn)槊餍堑膶B毷浅杌蜓輵蛏筒危绻殉艘酝獾钠渌虑楸热缪莩鲑M(fèi)用談判等等都攬?jiān)谏砩现玖麜鬯馈?
這就是體現(xiàn)一個(gè)思想:專業(yè)分工,用面向?qū)ο笮g(shù)語說:就是職責(zé)分明把篓。

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

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

No matter how slow you are writing clean code, you will always be slower if you make a mess 寫干凈的代碼無論有多慢阶界,總是快于把代碼寫得一團(tuán)糟。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末聋庵,一起剝皮案震驚了整個(gè)濱河市膘融,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祭玉,老刑警劉巖氧映,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脱货,居然都是意外死亡岛都,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門振峻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臼疫,“玉大人,你說我怎么就攤上這事扣孟√痰蹋” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長塔逃。 經(jīng)常有香客問我讯壶,道長,這世上最難降的妖魔是什么湾盗? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任伏蚊,我火速辦了婚禮,結(jié)果婚禮上格粪,老公的妹妹穿的比我還像新娘躏吊。我一直安慰自己,他們只是感情好帐萎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布比伏。 她就那樣靜靜地躺著,像睡著了一般疆导。 火紅的嫁衣襯著肌膚如雪赁项。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天澈段,我揣著相機(jī)與錄音悠菜,去河邊找鬼。 笑死败富,一個(gè)胖子當(dāng)著我的面吹牛悔醋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播兽叮,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼芬骄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鹦聪?” 一聲冷哼從身側(cè)響起账阻,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎椎麦,沒想到半個(gè)月后宰僧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡观挎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了段化。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘁捷。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖显熏,靈堂內(nèi)的尸體忽然破棺而出雄嚣,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布缓升,位于F島的核電站,受9級特大地震影響港谊,放射性物質(zhì)發(fā)生泄漏骇吭。R本人自食惡果不足惜歧寺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望斜筐。 院中可真熱鬧,春花似錦顷链、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽潭苞。三九已至,卻和暖如春此疹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蝗碎。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工湖笨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹦骑。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓慈省,卻偏偏與公主長得像,于是被迫代替她去往敵國和親眠菇。 傳聞我的和親對象是個(gè)殘疾皇子边败,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容