《設(shè)計(jì)模式之禪》讀書筆記-2.6-代理模式

2.6 代理模式

代理的原理可先參看此文章:Java基礎(chǔ)-001-枚舉壶熏、反射贬循、類加載器咸包、內(nèi)省、注解杖虾、泛型烂瘫、代理

定義:

Proxy Pattern,也叫委托模式
Provide a surrogate or placeholder for another object to control access to it
為其它對象提供一種代理以控制對這個(gè)對象的訪問
許多其他的模式奇适,如狀態(tài)模式坟比、策略模式、訪問者模式本質(zhì)上是在更特殊的場合采用了委托模式滤愕。

角色定義:

Subject抽象主題角色

可以是抽象類或接口温算,是一個(gè)最普通的業(yè)務(wù)類型定義,無特殊要求

RealSubject具體主題角色(真實(shí)角色)

也叫做被委托角色间影、被代理角色注竿。是業(yè)務(wù)邏輯的具體執(zhí)行者

Proxy代理主題角色

也叫做委托類、代理類魂贬。負(fù)責(zé)對真實(shí)角色的應(yīng)用巩割,把所有抽象主題類定義的方法限制委托給真實(shí)主題角色實(shí)現(xiàn),并在真實(shí)主題角色處理完畢前后做預(yù)處理和善后處理付燥。

優(yōu)點(diǎn):

  1. 職責(zé)清晰

真實(shí)角色實(shí)現(xiàn)實(shí)際的業(yè)務(wù)邏輯宣谈,不用關(guān)心其它非本職責(zé)的邏輯,后期通過代理完成一件事務(wù)键科,結(jié)果就是編程簡潔清晰

  1. 高擴(kuò)展性

具體主題角色可隨時(shí)發(fā)生變化闻丑。只要實(shí)現(xiàn)了抽象主題接口,代理類就不用再次修改勋颖。

  1. 智能化

動態(tài)代理

使用場景:

參考Spring AOP

2.6.1 擴(kuò)展-普通代理

客戶端只能訪問代理角色嗦嗡,不能訪問真實(shí)角色

代碼:

/**
 * 抽象角色
 */
interface IGamePlayer{
    public void login(String user, String password);//登錄
    public void killBoss();//打怪
    public void upgrade();//升級
}

/**
 * 真實(shí)角色
 */
class GamePlayer implements IGamePlayer{
    private String name;
    /**
     * 構(gòu)造函數(shù)限制誰能創(chuàng)建對象,并同時(shí)傳遞姓名
     * 還可以增加其它限制饭玲,比如類名必須為Proxy等
     * 
     * 在實(shí)際的項(xiàng)目中侥祭,一般都是通過約定來禁止new一個(gè)真實(shí)的角色
     */
    public GamePlayer(IGamePlayer gamePlayer, String name) throws Exception{
        if(gamePlayer == null){
            throw new Exception("不能創(chuàng)建真實(shí)角色");
        }else{
            this.name = name;
        }
    }
    
    @Override
    public void login(String user, String password) {
        System.out.println("登錄名:" + user +"; 密碼:" + password + "。登錄成功茄厘!");
    }
    @Override
    public void killBoss() {
        System.out.println(this.name + "在打怪矮冬!");
    }
    @Override
    public void upgrade() {
        System.out.println(this.name + "又升了一級!");
    }
}

/**
 * 代理類
 */
class GamePlayerProxy implements IGamePlayer{
    private IGamePlayer gamePlayer;
    public GamePlayerProxy(String name){
        try{
            gamePlayer = new GamePlayer(this, name);
        }catch(Exception e){
            //異常處理
        }
    }
    @Override
    public void login(String user, String password) {
        this.gamePlayer.login(user, password);
    }

    @Override
    public void killBoss() {
        this.gamePlayer.killBoss();
    }

    @Override
    public void upgrade() {
        this.gamePlayer.upgrade();
    }
}

/**
 * 場景類
 */
class Client{
    public static void main(String[] args) {
        IGamePlayer proxy = new GamePlayerProxy("張三");
        proxy.login("zhangsan", "123456");
        proxy.killBoss();
        proxy.upgrade();
    }
}

2.6.2 擴(kuò)展-強(qiáng)制代理

要求:

  1. 必須通過真實(shí)角色才能找到代理角色
  2. 直接通過代理類或new真實(shí)角色來調(diào)用方法都不能得到想要的結(jié)果
  3. 真實(shí)角色管理代理角色

代碼:

/**
 * 抽象角色
 */
interface IGamePlayer_Force{
    public void login(String user, String password);
    public void killBoss();
    public void upgrade();
    //每個(gè)類都可以找自己的代理
    public IGamePlayer_Force getProxy();
}
/**
 * 具體角色
 */
class GamePlayer_Force implements IGamePlayer_Force{
    private String name;
    private IGamePlayer_Force proxyForce;
    
    public GamePlayer_Force(String name){
        this.name = name;
    }

    @Override
    public void login(String user, String password) {
        if (this.isProxy()) {
            System.out.println("登錄名:" + user +"; 密碼:" + password + "次哈。登錄成功胎署!");
        } else {
            System.out.println("請使用指定的代理訪問");
        }
    }

    @Override
    public void killBoss() {
        if (this.isProxy()) {
            System.out.println(this.name + "在打怪!");
        } else {
            System.out.println("請使用指定的代理訪問");
        }
    }

    @Override
    public void upgrade() {
        if (this.isProxy()) {
            System.out.println(this.name + "又升了一級窑滞!");
        } else {
            System.out.println("請使用指定的代理訪問");
        }
    }

    @Override
    public IGamePlayer_Force getProxy() {
        this.proxyForce = new GamePlayerProxy_Force(this);
        return this.proxyForce;
    }
    
    // 校驗(yàn)是否是代理訪問
    private boolean isProxy() {
        if (this.proxyForce == null) {
            return false;
        } else {
            return true;
        }
    }
}


/**
 * 代理類
 */
class GamePlayerProxy_Force implements IGamePlayer_Force {
    private IGamePlayer_Force gamePlayerForce;
    
    public GamePlayerProxy_Force(IGamePlayer_Force gamePlayerForce){
        this.gamePlayerForce = gamePlayerForce;
    }

    @Override
    public void login(String user, String password) {
        this.gamePlayerForce.login(user, password);
    }

    @Override
    public void killBoss() {
        this.gamePlayerForce.killBoss();
    }

    @Override
    public void upgrade() {
        this.gamePlayerForce.upgrade();
    }

    //代理類的代理暫時(shí)沒有琼牧,返回自己
    @Override
    public IGamePlayer_Force getProxy() {
        return this;
    }
}

/**
 * 場景類
 */
class Client_Force {
    public static void main(String[] args) {
        IGamePlayer_Force player = new GamePlayer_Force("張三");
        IGamePlayer_Force proxy = player.getProxy();

        proxy.login("zhangSan", "password");
        proxy.killBoss();
        proxy.upgrade();
    }
}

2.6.3 擴(kuò)展-代理類的個(gè)性

interface IProxy {
    public void count(); //計(jì)算費(fèi)用
}

class GamePlayerProxy_Force implements IGamePlayer_Force, IProxy {
    //...
    
    @Override
    public void upgrade() {
        this.gamePlayerForce.upgrade();
        this.count();
    }
    @Override
    public void count() {
        System.out.println("升級費(fèi)用是:¥100");
    }
}

2.6.4 擴(kuò)展-動態(tài)代理

動態(tài)代理: 在實(shí)現(xiàn)階段不用關(guān)心代理是誰径筏,在運(yùn)行階段才指定代理哪一個(gè)類。

代碼一:參考代碼(引子)

interface IGamePlayer{
    public void login(String user, String password);
    public void killBoss();
    public void upgrade();
    public IGamePlayer getProxy();//每個(gè)類都可以找自己的代理
}

class GamePlayer implements IGamePlayer{
    private String name;
    private IGamePlayer proxyForce;
    
    public GamePlayer(String name){
        this.name = name;
    }
    //...
}

//動態(tài)代理
class GamePlayIH implements InvocationHandler {
    Object obj ; // 被代理的實(shí)例
    // 我要代理誰
    public GamePlayIH(Object _obj) {
        this.obj = _obj;
    }
    // 調(diào)用被代理的方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj, args);
        return result;
    }
}


class Client2 {
    public static void main(String[] args) throws Throwable {
        IGamePlayer player = new GamePlayer("張三");//對應(yīng)強(qiáng)制代理的GamePlayer_Force
        //定義一個(gè)handler
        InvocationHandler handler = new GamePlayIH(player);
        //獲得類的class loader
        ClassLoader cl = player.getClass().getClassLoader();
        //動態(tài)產(chǎn)生一個(gè)代理者
        IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(cl, 
                             new Class[]{IGamePlayer.class}, handler);
        proxy.login("zhangSan", "password");
        proxy.killBoss();//開始?xì)⒐?        proxy.upgrade();//升級
    }
}

代碼二:模板代碼(定義用)

/**
 * ?    ?     ?     ?     ?     ?    主題    ?    ?     ?     ?     ?     ?    
 */
//抽象主題
interface Subject{
    public void doSomething(String str);
}

//真實(shí)主題
class RealSubject implements Subject{
    public void doSomething(String str){
        System.out.println("doSomething: " + str);
    }
}

/**
 * ?    ?     ?     ?     ?     ?    動態(tài)代理的Handler    ?    ?     ?     ?     ?     ?    
 */
class MyInvocationHandler implements InvocationHandler{
    private Object target; //被代理對象
    
    public MyInvocationHandler(Object obj){
        this.target = obj;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target, args);
    }
}

/**
 * ?    ?     ?     ?     ?     ?    動態(tài)代理類    ?    ?     ?     ?     ?     ?    
 */
class DynamicProxy<T>{
    public static<T> T newProxyInstance(ClassLoader loader, 
                        Class<?>[] interfaces, InvocationHandler h){
        //尋找JoinPoint連接點(diǎn)
        if(true){
            (new BeforeAdvice()).exec();
        }
        return (T) Proxy.newProxyInstance(loader, interfaces, h);
    }
}

/**
 * ?    ?     ?     ?     ?     ?    通知    ?    ?     ?     ?     ?     ?    
 */
//通知接口
interface IAdvice{
    public void exec();
}

//前置通知
class BeforeAdvice implements IAdvice{
    public void exec(){
        System.out.println("我是前置通知");
    }
}

/**
 * ?    ?     ?     ?     ?     ?    補(bǔ)充:具體業(yè)務(wù)動態(tài)代理類    ?    ?     ?     ?     ?     ?    
 */
class SubjectDynamicProxy extends DynamicProxy{
    public static<T> T newProxyInstance(Subject subject){
        ClassLoader loader = subject.getClass().getClassLoader();
        Class<?>[] cls = subject.getClass().getInterfaces();
        InvocationHandler h = new MyInvocationHandler(subject);
        return newProxyInstance(loader, cls, h);
    }
}


/**
 * ?    ?     ?     ?     ?     ?    場景類    ?    ?     ?     ?     ?     ?    
 */
class ProxyClient{
    public static void main(String[] args) {
        //定義主題類
        Subject subject = new RealSubject();
        //定義主題類的代理
        Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
        proxy.doSomething("Finish");
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末障陶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子聊训,更是在濱河造成了極大的恐慌抱究,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件带斑,死亡現(xiàn)場離奇詭異鼓寺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)勋磕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門妈候,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挂滓,你說我怎么就攤上這事苦银。” “怎么了赶站?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵幔虏,是天一觀的道長。 經(jīng)常有香客問我贝椿,道長想括,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任烙博,我火速辦了婚禮瑟蜈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渣窜。我一直安慰自己铺根,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布图毕。 她就那樣靜靜地躺著夷都,像睡著了一般。 火紅的嫁衣襯著肌膚如雪予颤。 梳的紋絲不亂的頭發(fā)上囤官,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機(jī)與錄音蛤虐,去河邊找鬼党饮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛驳庭,可吹牛的內(nèi)容都是我干的刑顺。 我是一名探鬼主播氯窍,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蹲堂!你這毒婦竟也來了狼讨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤柒竞,失蹤者是張志新(化名)和其女友劉穎政供,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朽基,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡布隔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稼虎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衅檀。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖霎俩,靈堂內(nèi)的尸體忽然破棺而出哀军,到底是詐尸還是另有隱情,我是刑警寧澤打却,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布排苍,位于F島的核電站,受9級特大地震影響学密,放射性物質(zhì)發(fā)生泄漏淘衙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一腻暮、第九天 我趴在偏房一處隱蔽的房頂上張望彤守。 院中可真熱鬧,春花似錦哭靖、人聲如沸具垫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筝蚕。三九已至,卻和暖如春铺坞,著一層夾襖步出監(jiān)牢的瞬間起宽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工济榨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坯沪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓擒滑,卻偏偏與公主長得像腐晾,于是被迫代替她去往敵國和親叉弦。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

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