<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「補(bǔ)課」進(jìn)行時(shí):設(shè)計(jì)模式(5)——從 LOL 中學(xué)習(xí)代理模式

          共 9882字,需瀏覽 20分鐘

           ·

          2020-11-01 07:27

          1. 從 LOL 中學(xué)習(xí)代理模式

          我是一個(gè)很喜歡玩游戲的人,雖然平時(shí)玩游戲的時(shí)間并不多,但我也是一個(gè)忠實(shí)的 LOL 的愛好者,就是段位有點(diǎn)慘不忍睹,常年倔強(qiáng)的黑鐵,今年 S10 的總決賽在上海舉行,這個(gè)事兒我從 S9 就開始期待,結(jié)果門票今年沒賣,直接是抽簽拼人品。

          360w+ 人抽 3600+ 人,這個(gè)概率屬實(shí)有點(diǎn)低,只能找個(gè)地方和我的小伙伴一起看了。

          打 LOL 最開心的事情莫過于拿到 PentaKill 和 victory ,把這件事情使用代碼表現(xiàn)出來,首先定義一個(gè)玩游戲的人的接口:

          public?interface?ILOLPlayer?{
          ????//?登錄使用用戶名和密碼
          ????void?login(String?name,?String?password);
          ????//?拿到五殺
          ????void?pentaKill();
          ????//?游戲勝利
          ????void?victory();
          }

          第二步對(duì)上面的接口做一個(gè)實(shí)現(xiàn):

          public?class?LOLPlayer?implements?ILOLPlayer?{

          ????private?String?name?=?"";

          ????public?LOLPlayer(String?name)?{
          ????????this.name?=?name;
          ????}

          ????@Override
          ????public?void?login(String?name,?String?password)?{
          ????????System.out.println("登錄游戲:name:"?+?name?+?",?password:"?+?password);
          ????}

          ????@Override
          ????public?void?pentaKill()?{
          ????????System.out.println(this.name?+?"?拿到五殺啦?。?!");
          ????}

          ????@Override
          ????public?void?victory()?{
          ????????System.out.println(this.name?+?"?游戲勝利啦!??!");
          ????}
          }

          最后我們寫一個(gè)最簡單的測試類:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????LOLPlayer?lolPlayer?=?new?LOLPlayer("geekdigging");
          ????????lolPlayer.login("geekdigging",?"password");
          ????????lolPlayer.pentaKill();
          ????????lolPlayer.victory();
          ????}
          }

          運(yùn)行結(jié)果:

          登錄游戲:name:geekdigging, password:password
          geekdigging 拿到五殺啦!??!
          geekdigging 游戲勝利啦?。?!

          在打游戲的過程中,大家都知道有一個(gè)類型叫做排位賽,排位賽能到多少段位,一個(gè)是看時(shí)間,一個(gè)是看天賦,基本上打到一定的段位就很難再往上走了,如果說這時(shí)候還想升段位,那就只能取找代練幫忙做代打了。

          我們找一位代練幫我們繼續(xù)打游戲:

          public?class?LOLPlayerProxy?implements?ILOLPlayer?{

          ????private?ILOLPlayer?ilolPlayer;

          ????public?LOLPlayerProxy(LOLPlayer?playerLayer)?{
          ????????this.ilolPlayer?=?playerLayer;
          ????}

          ????@Override
          ????public?void?login(String?name,?String?password)?{
          ????????this.ilolPlayer.login(name,?password);
          ????}

          ????@Override
          ????public?void?pentaKill()?{
          ????????this.ilolPlayer.pentaKill();
          ????}

          ????@Override
          ????public?void?victory()?{
          ????????this.ilolPlayer.victory();
          ????}
          }

          我們稍微修改一下測試類:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????LOLPlayer?lolPlayer?=?new?LOLPlayer("geekdigging");
          ????????LOLPlayerProxy?proxy?=?new?LOLPlayerProxy(lolPlayer);
          ????????proxy.login("geekdigging",?"password");
          ????????proxy.pentaKill();
          ????????proxy.victory();
          ????}
          }

          這個(gè)測試類里面,我們沒有自己打游戲,而是使用代練 proxy 來幫我們打游戲,最后的結(jié)果是:

          登錄游戲:name:geekdigging, password:password
          geekdigging 拿到五殺啦?。。?br>geekdigging 游戲勝利啦?。。?br>

          這就是代理模式,本來需要自己做事情,使用代理以后,就可以由代理幫我們做事情了。

          2. 代理模式定義

          代理模式(Proxy Pattern)是一個(gè)使用率非常高的模式,其定義如下:

          Provide a surrogate or placeholder for another object to control access toit.(為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。)

          • Subject: 抽象主題角色。
          • RealSubject: 具體主題角色。
          • Proxy: 代理主題角色。

          通用示例代碼如下:

          //?抽象主題類,定義一個(gè)方法
          public?interface?Subject?{
          ????void?request();
          }

          //?具體主題類,在這里寫具體的處理邏輯
          public?class?RealSubject?implements?Subject?{
          ????@Override
          ????public?void?request()?{
          ????????//?邏輯處理
          ????}
          }

          //?代理類
          public?class?Proxy?implements?Subject?{

          ????private?Subject?subject;

          ????public?Proxy()?{
          ????????this.subject?=?new?Proxy();
          ????}

          ????public?Proxy(RealSubject?subject)?{
          ????????this.subject?=?subject;
          ????}

          ????@Override
          ????public?void?request()?{
          ????????this.before();
          ????????this.subject.request();
          ????????this.after();
          ????}

          ????private?void?before()?{
          ????????//?邏輯預(yù)處理
          ????}

          ????private?void?after()?{
          ????????//?邏輯善后處理
          ????}
          }

          在最后的這個(gè)代理類中,通過構(gòu)造函數(shù)來進(jìn)行代理角色的傳遞,同時(shí)還可以在具體的處理邏輯上構(gòu)造一個(gè)切面,定義預(yù)處理邏輯以及善后處理邏輯。

          3. 代理模式的優(yōu)點(diǎn)

          1. 職責(zé)清晰:真實(shí)的角色是用來實(shí)現(xiàn)具體業(yè)務(wù)邏輯的,無需關(guān)心其他工作,可以后期通過代理的方式來完成其他的工作。
          2. 高擴(kuò)展性:
          3. 智能化:

          4. 普通代理

          首先說普通代理,它的要求就是客戶端只能訪問代理角色,而不能訪問真實(shí)角色,這是比較簡單的。

          使用上面最開始的打 LOL 進(jìn)行改造,我自己作為一個(gè)游戲玩家,我肯定自己不練級(jí)了,也就是場景類不能再直接 new 一個(gè) LOLPlayer 對(duì)象了,它必須由 LOLPlayerProxy 來進(jìn)行模擬場景。

          首先是對(duì) LOLPlayer 類進(jìn)行改造,把 LOLPlayer 這個(gè)類的構(gòu)造方法修改,使他不能直接 new 一個(gè)對(duì)象出來。

          public?class?LOLPlayer?implements?ILOLPlayer?{

          ????private?String?name;

          ????public?LOLPlayer(ILOLPlayer?ilolPlayer,?String?name)?throws?Exception?{
          ????????if?(ilolPlayer?==?null)?{
          ????????????throw?new?Exception("不能創(chuàng)建真實(shí)的角色");
          ????????}?else?{
          ????????????this.name?=?name;
          ????????}
          ????}
          ????//?省略剩余的代碼
          }

          接下來是代理類:

          public?class?LOLPlayerProxy?implements?ILOLPlayer?{

          ????private?ILOLPlayer?iloLPlayer;

          ????public?LOLPlayerProxy(String?name)?{
          ????????try?{
          ????????????iloLPlayer?=?new?LOLPlayer(this,?name);
          ????????}?catch?(Exception?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}

          ????//?省略剩余的代碼
          }

          代理類也是僅修改了構(gòu)造函數(shù),通過傳進(jìn)來的一個(gè)代理者的名稱,就能進(jìn)行代理,在這種改造下,系統(tǒng)更加簡潔了,調(diào)用者只知道代理存在就可以,不用知道代理了誰。

          最后的測試類也需要進(jìn)行修改:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????ILOLPlayer?proxy?=?new?LOLPlayerProxy("geekdigging");
          ????????proxy.login("geekdigging",?"password");
          ????????proxy.pentaKill();
          ????????proxy.victory();
          ????}
          }

          在這個(gè)代理類上,我沒有再去 new 一個(gè) LOLPlayer 的對(duì)象,即可對(duì) LOLPlayer 進(jìn)行代理。

          5. 強(qiáng)制代理

          強(qiáng)制代理實(shí)際上一個(gè)普通代理模式的變種,普通代理是通過代理找到真實(shí)的角色,但是強(qiáng)制代理卻是要「強(qiáng)制」,必須通過真實(shí)角色查找到代理角色,否則將不能訪問。

          首先是對(duì)接口類加一個(gè) getProxy() 方法,指定要訪問自己必須通過哪個(gè)代理。

          public?interface?ILOLPlayer?{
          ????//?登錄使用用戶名和密碼
          ????void?login(String?name,?String?password);
          ????//?拿到五殺
          ????void?pentaKill();
          ????//?游戲勝利
          ????void?victory();
          ????//?獲取自己的代理類
          ????ILOLPlayer?getProxy();
          }

          然后再是對(duì)具體實(shí)現(xiàn)類的改造:

          public?class?LOLPlayer?implements?ILOLPlayer?{

          ????private?String?name;

          ????private?ILOLPlayer?proxy;

          ????public?LOLPlayer(String?name)?{
          ????????this.name?=?name;
          ????}

          ????@Override
          ????public?void?login(String?name,?String?password)?{
          ????????if?(this.isProxy())?{
          ????????????System.out.println("登錄游戲:name:"?+?name?+?",?password:"?+?password);
          ????????}?else?{
          ????????????System.out.println("請(qǐng)使用指定的代理");
          ????????}

          ????}

          ????@Override
          ????public?void?pentaKill()?{
          ????????if?(this.isProxy())?{
          ????????????System.out.println(this.name?+?"?拿到五殺啦?。?!");
          ????????}?else?{
          ????????????System.out.println("請(qǐng)使用指定的代理");
          ????????}
          ????}

          ????@Override
          ????public?void?victory()?{
          ????????if?(this.isProxy())?{
          ????????????System.out.println(this.name?+?"?游戲勝利啦!??!");
          ????????}?else?{
          ????????????System.out.println("請(qǐng)使用指定的代理");
          ????????}
          ????}

          ????@Override
          ????public?ILOLPlayer?getProxy()?{
          ????????this.proxy?=?new?LOLPlayerProxy(this);
          ????????return?this.proxy;
          ????}

          ????private?boolean?isProxy()?{
          ????????if?(this.proxy?==?null)?{
          ????????????return?false;
          ????????}?else?{
          ????????????return?true;
          ????????}
          ????}
          }

          這里增加了一個(gè)私有方法,檢查是否是自己指定的代理,是指定的代理則允許訪問,否則不允許訪問。

          接下來是強(qiáng)制代理類的改進(jìn):

          public?class?LOLPlayerProxy?implements?ILOLPlayer?{

          ????private?ILOLPlayer?iloLPlayer;

          ????public?LOLPlayerProxy(ILOLPlayer?iloLPlayer)?{
          ????????this.iloLPlayer?=?iloLPlayer;
          ????}

          ????@Override
          ????public?void?login(String?name,?String?password)?{
          ????????this.iloLPlayer.login(name,?password);
          ????}

          ????@Override
          ????public?void?pentaKill()?{
          ????????this.iloLPlayer.pentaKill();
          ????}

          ????@Override
          ????public?void?victory()?{
          ????????this.iloLPlayer.victory();
          ????}

          ????@Override
          ????public?ILOLPlayer?getProxy()?{
          ????????return?this;
          ????}
          }

          最后一個(gè)是測試類:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????test1();
          ????????test2();
          ????????test3();
          ????}

          ????public?static?void?test1()?{
          ????????ILOLPlayer?iloLPlayer?=?new?LOLPlayer("geekdigging");
          ????????iloLPlayer.login("geekdigging",?"password");
          ????????iloLPlayer.pentaKill();
          ????????iloLPlayer.victory();
          ????}

          ????public?static?void?test2()?{
          ????????ILOLPlayer?iloLPlayer?=?new?LOLPlayer("geekdigging");
          ????????ILOLPlayer?proxy?=?new?LOLPlayerProxy(iloLPlayer);
          ????????proxy.login("geekdigging",?"password");
          ????????proxy.pentaKill();
          ????????proxy.victory();
          ????}

          ????public?static?void?test3()?{
          ????????ILOLPlayer?iloLPlayer?=?new?LOLPlayer("geekdigging");
          ????????ILOLPlayer?proxy?=?iloLPlayer.getProxy();
          ????????proxy.login("geekdigging",?"password");
          ????????proxy.pentaKill();
          ????????proxy.victory();
          ????}
          }

          這里我寫了三個(gè)測試方法,分別是 test1 、 test2 和 test3 ,執(zhí)行一下這個(gè)測試類,結(jié)果如下:

          請(qǐng)使用指定的代理
          請(qǐng)使用指定的代理
          請(qǐng)使用指定的代理
          請(qǐng)使用指定的代理
          請(qǐng)使用指定的代理
          請(qǐng)使用指定的代理
          登錄游戲:name:geekdigging, password:password
          geekdigging 拿到五殺啦?。?!
          geekdigging 游戲勝利啦!?。?br>

          可以發(fā)現(xiàn),前兩個(gè)方法都沒有正常產(chǎn)生訪問, test1 是直接 new 了一個(gè)對(duì)象,無法成功訪問,而 test2 雖然是使用了代理,但是結(jié)果還是失敗了,因?yàn)樗付ǖ牟⒉皇钦鎸?shí)的對(duì)象,這個(gè)對(duì)象是我們自己手動(dòng) new 出來的,當(dāng)然不行,只有最后一個(gè) test3 是可以正常代理對(duì)象的。

          強(qiáng)制代理的概念就是要從真實(shí)角色查找到代理角色,不允許直接訪問真實(shí)角色。高層模塊只要調(diào)用 getProxy 就可以訪問真實(shí)角色的所有方法,它根本就不需要產(chǎn)生一個(gè)代理出來,代理的管理已經(jīng)由真實(shí)角色自己完成。

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

          動(dòng)態(tài)代理是在實(shí)現(xiàn)階段不用關(guān)心代理誰,而在運(yùn)行階段才指定代理哪一個(gè)對(duì)象。相對(duì)來說,自己寫代理類的方式就是靜態(tài)代理。

          實(shí)現(xiàn)動(dòng)態(tài)代理,主要有兩種方式,一種是通過 JDK 為我們提供的 InvocationHandler 接口,另一種是使用 cglib 。

          把上面的案例接著改成動(dòng)態(tài)代理的方式:

          增加一個(gè) LOLPlayIH 動(dòng)態(tài)代理類,來實(shí)現(xiàn) InvocationHandler 接口。

          public?class?LOLPlayIH?implements?InvocationHandler?{

          ????Object?object;

          ????public?LOLPlayIH(Object?object)?{
          ????????this.object?=?object;
          ????}

          ????@Override
          ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
          ????????Object?result?=?method.invoke(this.object,?args);
          ????????return?result;
          ????}
          }

          這里的 invoke 方法是接口 InvocationHandler 定義必須實(shí)現(xiàn)的,它完成對(duì)真實(shí)方法的調(diào)用。

          接下來是測試類:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????ILOLPlayer?ilolPlayer?=?new?LOLPlayer("geekdigging");
          ????????InvocationHandler?handler?=?new?LOLPlayIH(ilolPlayer);
          ????????ClassLoader?loader?=?ilolPlayer.getClass().getClassLoader();
          ????????ILOLPlayer?proxy?=?(ILOLPlayer)?Proxy.newProxyInstance(loader,?new?Class[]?{ILOLPlayer.class},?handler);
          ????????proxy.login("geekdigging",?"password");
          ????????proxy.pentaKill();
          ????????proxy.victory();
          ????}
          }

          這里我們沒有創(chuàng)建代理類,也沒有實(shí)現(xiàn) ILOLPlayer 接口,但我們還是讓代練在幫我們上分,這就是動(dòng)態(tài)代理。

          接下來看下 CGLIB 代理的方式,修改前面的代理類:

          public?class?CglibProxy?implements?MethodInterceptor?{

          ????private?Object?target;
          ????public?Object?getInstance(final?Object?target)?{
          ????????this.target?=?target;
          ????????Enhancer?enhancer?=?new?Enhancer();
          ????????enhancer.setSuperclass(this.target.getClass());
          ????????enhancer.setCallback(this);
          ????????return?enhancer.create();
          ????}

          ????@Override
          ????public?Object?intercept(Object?o,?Method?method,?Object[]?objects,?MethodProxy?methodProxy)?throws?Throwable?{

          ????????Object?result?=?methodProxy.invoke(this.target,?objects);

          ????????return?result;
          ????}
          }

          編寫新的測試類:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????ILOLPlayer?ilolPlayer?=?new?LOLPlayer("geekdigging");
          ????????CglibProxy?proxy?=?new?CglibProxy();
          ????????LOLPlayer?lolPlayer?=?(LOLPlayer)?proxy.getInstance(ilolPlayer);
          ????????lolPlayer.login("geekdigging",?"password");
          ????????lolPlayer.pentaKill();
          ????????lolPlayer.victory();
          ????}
          }

          這里有一點(diǎn)需要注意, CGLIB 動(dòng)態(tài)代理需要具體對(duì)象擁有無參構(gòu)造,需要我們手動(dòng)在 LOLPlayer 中添加一個(gè)無參構(gòu)造函數(shù)。





          感謝閱讀



          瀏覽 29
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲秘 无码一区二区 | 极品外围女在线 | 成人视频自拍偷拍 | 欧美在线大屌视频 | 殴美一级黑人 |