Java設計模式 — Decorator(裝飾)邓厕,Delegation(委托) ,Proxy(代理)

前言

為什么要將Decorator(裝飾)扁瓢,Delegation(委托) 详恼,Proxy(代理)這三個模式放在一起呢?
因為它們的代碼是如此地相似涤妒。如果不結合場景单雾,會很容易分不清楚赚哗。
所以請在本文中嘗試體會它們當中的細微差別她紫。

我們應該記住,設計模式并非無中生有屿储,其原型往往來源于生活贿讹。
在面對對象編程(OOP)時,我們的代碼可以看作現(xiàn)實世界某些場景的縮影够掠,我們的對象則可以看作是場景中的一個個人民褂。

因此看似相似的代碼所還原出的應用場景并不一定相同,它也就被抽象成不同的模式。

意圖

  • Decorator(裝飾)
    側重于為一個基礎對象動態(tài)地增強它的職責或能力赊堪。
    這是面對排列組合所造成的子類數(shù)爆炸問題的一種靈活的解決方案面殖。

  • Delegation(委托)
    側重于對用戶提供統(tǒng)一的接口,卻可以切換多種底層實現(xiàn)哭廉。
    當使用委托對象的某個方法時脊僚,它不并自己實現(xiàn),而是往后退遵绰,委托給其內部的被委托對象讓其代勞辽幌。

  • Proxy(代理)
    側重于控制訪問。
    不改變被代理對象的職責或能力椿访。提供與被代理對象相同的接口乌企,但會添加一些特有的邏輯來控制對被代理對象的訪問。

生動的栗子

有一個王國要打仗了成玫!王國的武器庫里有很多武器加酵。
比如說,有劍有斧子梁剔!

public interface Weapon {
    int makeDamage();
}

public class Sword implements Weapon {
    private int att = 10;

    public int makeDamage() {
        return this.att;
    }
}


public class Axe implements Weapon {
    private int att = 15;

    public int makeDamage() {
        return this.att;
    }
}

斧子的初始攻擊比劍稍微高點虽画。但是沒關系,我們可以給它們加很多其他屬性荣病。比如說码撰,可以給武器加點毒屬性,也可以了來點鋼屬性个盆〔钡海總有一款適合你。

public abstract class WeaponDecorator implements Weapon {
    protected Weapon weaponDecorated;

    public WeaponDecorator(Weapon weaponDecorated) {
        this.weaponDecorated = weaponDecorated;
    }

    public abstract int makeDamage();
}


public class PoisonWeapon extends WeaponDecorator{
    private int powerOfPoison = 10;

    public PoisonWeapon(Weapon weaponDecorated) {
        super(weaponDecorated);
    }

    @Override
    public int makeDamage() {
        return weaponDecorated.makeDamage() + powerOfPoison;
    }
}


public class SteelWeapon extends WeaponDecorator{
    private int powerOfSteel = 12;

    public SteelWeapon(Weapon weaponDecorated) {
        super(weaponDecorated);
    }

    @Override
    public int makeDamage() {
        return weaponDecorated.makeDamage() + powerOfSteel;
    }
}

我們就可以拿這些屬性來裝飾實際的武器颊亮。
理論上我們一共可能得到這么多種武器柴梆。

    Weapon normalSword = new Sword(); // 普通劍
    Weapon steelSword = new SteelWeapon(new Sword()); //鋼劍
    Weapon poisonSword = new PoisonWeapon(new Sword()); //毒劍
    Weapon poisonSteelSword = new PoisonWeapon(new SteelWeapon(new Sword())); //毒鋼劍
    Weapon normalAxe = new Axe(); // 普通斧
    Weapon steelAxe = new SteelWeapon(new Axe()); //鋼斧
    Weapon poisonAxe = new PoisonWeapon(new Axe()); //毒斧
    Weapon poisonSteelAxe = new PoisonWeapon(new SteelWeapon(new Axe())); //毒鋼斧

我們忽略了屬性之間的順序,但有些時候順序是有意義的终惑,那么種類就會更多绍在。
這就是Decorator(裝飾)模式了。
2個基類加上2個裝飾器雹有,我們得到了8種結果偿渡。如果又多了1個基類,又多了2個裝飾器呢霸奕?在不考慮順序的情況下溜宽,有48種結果。
排列組合的威力太過強大质帅,如果不用裝飾模式适揉,我們得寫48個子類出來留攒。
但有了裝飾器,我們只需要7個類而已嫉嘀。而且可以非常靈活得組裝炼邀。

接著說故事~

我們的主角獸人大兄弟登場了。
他想要為王國效力剪侮。出戰(zhàn)前他可以去武器庫選把武器汤善。

public interface Warrior {
    void attack();
    int damageValue();
}


public class OrcWarrior implements Warrior {
    private WeaponArsenal weaponArsenal = new WeaponArsenal();

    public void attack() {
        System.out.println(String.format("洛克打猴哥!造成%d點傷害!", damageValue()));
    }

    public int damageValue() {
        return weaponArsenal.makeDamage();
    }

    public void selectWeapon(WeaponType weaponType) {
         weaponArsenal.setWeaponType(weaponType);
    }
}


public enum WeaponType {
    SWORD, AXE
}


public class WeaponArsenal implements Weapon {
    private WeaponType weaponType;

    public int makeDamage() {
        Weapon weapon = getWeapon();
        if (weapon != null) {
            return weapon.makeDamage();
        }
        return 0;
    }

    public void setWeaponType(WeaponType weaponType) {
        this.weaponType = weaponType;
    }

    private Weapon getWeapon() {
        switch (weaponType) {
            case SWORD : return new SteelWeapon(new PoisonWeapon(new Sword()));
            case AXE : return new SteelWeapon(new Axe());
            default : return null;
        }
    }
}

這里就用到了Delegation(委托)模式票彪。
當從武器庫選擇了武器的獸人戰(zhàn)士想要造成傷害時红淡,他委托了武器庫去造成傷害——weaponArsenal.makeDamage()
但武器庫怎么可能去造成傷害呢降铸?武器庫其實又是委托了具體被選擇的那把武器來造成傷害——weapon.makeDamage()在旱。

獸人大兄弟十分英勇,但還是有時候贏有時候輸推掸。直到有一天他遇到了一個小法師桶蝎。
小法師完全不會打架,但智商很高谅畅。他發(fā)現(xiàn)我們這位獸人大兄弟看到什么都忍不住想上去砍一刀登渣,于是提議,由他來判斷形勢毡泻,可以打的時候就讓獸人兄弟上胜茧,打不了咱們就跑。

獸人兄弟沒多想仇味,一口答應呻顽。于是乎,小法師成為了獸人戰(zhàn)士的代理(Proxy)丹墨。

public class LittleWizard implements Warrior {
    private Warrior warrior;

    public LittleWizard(Warrior warrior) {
        this.warrior = warrior;
    }

    public void attack() {
        if (warrior.damageValue() < 30) {
            System.out.println("就這點攻擊力咱還是跑吧廊遍。");
        } else {
            System.out.println("不要慫,就是干贩挣!");
            warrior.attack();
        }
    }

    public int damageValue() {
        return 0; // Little wizard doesn't know how to fight
    }
}

有了小法師以后喉前,獸人的輸出被控制了,有了選擇性王财。
具體能不能打小法師說了算卵迂。

整個故事在戰(zhàn)場上是這樣的。
public class BattleField {
    public static void main(String[] args) {
        // 獸人大兄弟踏上了戰(zhàn)場
        Warrior orcWarrior = new OrcWarrior();
        // 選了把趁手的斧子
        ((OrcWarrior) orcWarrior).selectWeapon(WeaponType.AXE);
        // 遇到誰都上去砍
        orcWarrior.attack();
        // 遇到了小法師搪搏,小法師成了獸人戰(zhàn)士的代理人
        Warrior littleWizard = new LittleWizard(orcWarrior);
        // 遇到敵人了狭握,慫了
        littleWizard.attack();
        // 趕緊回去換把武器
        ((OrcWarrior) orcWarrior).selectWeapon(WeaponType.SWORD);
        // 上闪金!
        littleWizard.attack();
    }
}

結果是這樣的疯溺。

洛克打猴哥论颅!造成27點傷害!
就這點攻擊力咱還是跑吧。
不要慫囱嫩,就是干恃疯!
洛克打猴哥!造成32點傷害!

結論

上面雖然是一個魔幻現(xiàn)實主義場景墨闲,但我們會發(fā)現(xiàn)今妄,沒有一個模式是我們在生活中無法還原出來的,只是我們平時沒有認出它們鸳碧。
所以說設計模式源于生活盾鳞,而反過來將其還原成現(xiàn)實場景,就可以幫助我們更好地理解瞻离。

現(xiàn)實應用

  1. Decorator(裝飾)模式
    • 女生們每天早上不同的衣服搭配
    • Java中的FileInputStream()那一系列類
  2. Delegation(委托)模式
    • 實在太過常見腾仅,因為我們經常把自己的某些工作委托給在那方面更擅長的人去做。比如說外包業(yè)務套利。
  3. Proxy(代理)模式
    • 限制訪問型——比如經紀人和明星推励。錢不到位我們是不能上的!
    • 幫助訪問型——比如VPN肉迫,我們雖然訪問不了嗶~嗶验辞,但是我們可以訪問VPN,而VPN又可以訪問嗶~嗶喊衫。
    • 各司其職型——這種情況下的代理模式和委托模式很相似跌造。還是比如經紀人和明星。經紀人負責找演出族购,明星負責唱歌跳舞鼻听。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市联四,隨后出現(xiàn)的幾起案子撑碴,更是在濱河造成了極大的恐慌,老刑警劉巖朝墩,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醉拓,死亡現(xiàn)場離奇詭異,居然都是意外死亡收苏,警方通過查閱死者的電腦和手機亿卤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹿霸,“玉大人排吴,你說我怎么就攤上這事∨呈螅” “怎么了钻哩?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵屹堰,是天一觀的道長。 經常有香客問我街氢,道長扯键,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任珊肃,我火速辦了婚禮荣刑,結果婚禮上,老公的妹妹穿的比我還像新娘伦乔。我一直安慰自己厉亏,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布烈和。 她就那樣靜靜地躺著叶堆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斥杜。 梳的紋絲不亂的頭發(fā)上虱颗,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音蔗喂,去河邊找鬼忘渔。 笑死,一個胖子當著我的面吹牛缰儿,可吹牛的內容都是我干的畦粮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼乖阵,長吁一口氣:“原來是場噩夢啊……” “哼宣赔!你這毒婦竟也來了?” 一聲冷哼從身側響起瞪浸,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤儒将,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后对蒲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钩蚊,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年蹈矮,在試婚紗的時候發(fā)現(xiàn)自己被綠了砰逻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡泛鸟,死狀恐怖蝠咆,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情北滥,我是刑警寧澤刚操,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布闸翅,位于F島的核電站,受9級特大地震影響赡茸,放射性物質發(fā)生泄漏。R本人自食惡果不足惜祝闻,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一占卧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧联喘,春花似錦华蜒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蓖谢,卻和暖如春捂蕴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背闪幽。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工啥辨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盯腌。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓溉知,卻偏偏與公主長得像,于是被迫代替她去往敵國和親腕够。 傳聞我的和親對象是個殘疾皇子级乍,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容

  • 【學習難度:★★★☆☆,使用頻率:★★★★☆】直接出處:代理模式梳理和學習:https://github.com/...
    BruceOuyang閱讀 1,713評論 0 1
  • 目錄:1.代理模式定義&實現(xiàn)2.裝飾模式定義&實現(xiàn)3.靜態(tài)代理4.動態(tài)代理:JDK動態(tài)代理帚湘、Cglib動態(tài)代理5....
    lbcBoy閱讀 1,583評論 2 3
  • 簡介 代理模式和裝飾者模式是兩種常見的設計模式玫荣。代理模式是為其它對象提供一種代理以控制對這個對象的訪問。在某些情況...
    然則閱讀 726評論 2 3
  • 裝飾器模式可以在不修改代碼的情況下靈活的為一對象添加行為和職責大诸。當你要修改一個被其它類包含的類的行為時崇决,它可以代替...
    泥孩兒0107閱讀 280評論 0 0
  • 連綿雨水的灑落 使心情低落 有時 又因些許事糾結 一曲歌 帶來快樂 一首詩 尋找一片寧靜 此時 唯有歌曲和詩 更懂我
    我的青春腳步閱讀 299評論 1 7