Android設(shè)計模式-裝飾者模式

1、定義

裝飾者模式動態(tài)地將責(zé)任附加到對象上陨溅。如果要擴展功能终惑,裝飾者提供了比繼承更有彈性的替代方案。

2门扇、UML類圖

裝飾者模式.png

角色說明

  • Component:抽象組件雹有,接口或抽象類,被裝飾者臼寄。
  • ConcreteComponent:具體組件
  • Decorator:裝飾者抽象類霸奕,繼承被裝飾者的同時關(guān)聯(lián)被裝飾者(持有引用),同時添加自己的行為
  • ConcreteDecorator:具體裝飾類吉拳。

3质帅、模式演進

以星巴克為例,有多種類型咖啡留攒,特濃咖啡煤惩、混合咖啡、烘焙咖啡等炼邀。這里創(chuàng)建一個咖啡的基類魄揉,包含description與cost屬性。每種類型的咖啡可能會添加多種不同的調(diào)料拭宁,比如牛奶洛退、摩卡瓣俯、泡沫....

//基類
abstract class Beverage {
    private String description;
    public String getDescription() {
        return description;
    }
    public Beverage(String description) {
        this.description = description;
    }
    abstract public double cost();
}
class Espresso extends Beverage{
    public Espresso() {
        super("特濃咖啡");
    }
    @Override
    public double cost() {
        return 25;
    }
}

class HouseBlend extends Beverage{
    public HouseBlend() {
        super("混合咖啡");
    }
    @Override
    public double cost() {
        return 35;
    }
}

class DarkRoast extends Beverage{
    public DarkRoast() {
        super("烘焙咖啡");
    }
    @Override
    public double cost() {
        return 28;
    }
}
演進一

如果為第種類型的不同口味的咖啡都創(chuàng)建一個子類,可能會引起類爆炸(加1份奶兵怯,1份摩卡彩匕,2份奶......),對于維護來說是一種惡夢摇零。

演進二

使用實例變量和繼承來追蹤這些調(diào)料,cost不再是抽象方法

abstract class Beverage {
    private boolean milk,mocha,whip;//牛奶推掸,摩卡,泡沫
    private String description;
    public Beverage(String description) {
        this.description = description;
    }
    public boolean isMilk() {
        return milk;
    }
    public void setMilk(boolean milk) {
        this.milk = milk;
    }
    public boolean isMocha() {
        return mocha;
    }
    public void setMocha(boolean mocha) {
        this.mocha = mocha;
    }
    public boolean isWhip() {
        return whip;
    }
    public void setWhip(boolean whip) {
        this.whip = whip;
    }
    public String getDescription() {
        if(isMilk()) {
            description += " 牛奶";
        }
        if(isMocha()) {
            description += " 摩卡";
        }
        if(isWhip()) {
            description += " 泡沫";
        }
        return description;
    }
    public double cost() {
        double total = 0;
        if(isMilk()) {
            total += 2;
        }
        if(isMocha()) {
            total += 4;
        }
        if(isWhip()) {
            total += 3.5;
        }
        return total;
    };
}
class Espresso extends Beverage {
    public Espresso() {
        super("特濃咖啡");
    }
    @Override
    public double cost() {
        return 25 +  super.cost();
    }
}
class HouseBlend extends Beverage {
    public HouseBlend() {
        super("混合咖啡");
    }
    @Override
    public double cost() {
        return 35 +  super.cost();
    }
}
class DarkRoast extends Beverage {
    public DarkRoast() {
        super("烘焙咖啡");
    }
    @Override
    public double cost() {
        return 28 +  super.cost();
    }
}

客戶端測試

Beverage b = new HouseBlend();
b.setMilk(true);
System.out.println(b.getDescription() + ":" + b.cost());

Beverage b2 = new Espresso();
b2.setMocha(true);
System.out.println(b2.getDescription() + ":"+ b2.cost());

Beverage b3 = new DarkRoast();
b3.setWhip(true);
System.out.println(b3.getDescription() + ":"+ b3.cost());

輸出結(jié)果

混合咖啡 牛奶:37.0
特濃咖啡 摩卡:29.0
烘焙咖啡 泡沫:31.5
  • 優(yōu)點:
  1. 類不會爆炸
  2. 增加一種類型的咖啡時驻仅,不影響以前的代碼谅畅,符合開閉原則
class Decaf extends Beverage{
    public Decaf() {
        super("低咖啡因咖啡");
    }
    @Override
    public double cost() {
        return 29 + super.cost();
    }
}
  • 缺點:
    1、增加一種新的調(diào)料時噪服,需要修改Beverage的cost方法毡泻,不符合開閉原則
    2、如果想要加2份牛奶粘优,貌似比較麻煩
演進三 裝飾者模式

以咖啡為主體仇味,在運行時以調(diào)料來“裝飾”咖啡。比方說雹顺,顧客想要摩卡和牛奶烘焙咖啡丹墨。那么要做的是

  1. 拿一個烘焙咖啡對象(DarkRoast);
  2. 以摩卡對象(Mocha)裝飾它嬉愧,變成摩卡烘焙咖啡贩挣,加上摩卡的價格;
  3. 以牛奶對象(Milk)裝飾摩卡烘焙咖啡没酣,變成摩卡和牛奶烘焙咖啡王财,加上牛奶的價格;

如果要雙份牛奶裕便,就進行下一步

  1. 以牛奶對象(Milk)裝飾牛奶和摩卡烘焙咖啡绒净,變成摩卡和牛奶和牛奶烘焙咖啡,再加上一份牛奶的價格偿衰;

通過以上步驟分析:

  • 摩卡和牛奶是“裝飾者”挂疆,烘焙咖啡就是“被裝飾者”;
  • 裝飾完的對象可以繼續(xù)裝飾下翎,說明裝飾者與被裝飾者的類型是一致的囱嫩;
  • 裝飾者可以在被裝飾者的行為基礎(chǔ)上添加自己的行為(例如在烘焙咖啡基礎(chǔ)加上摩卡的價格),這就需要裝飾者持有被裝飾者的引用
  • 可以運行時動態(tài)地裝飾對象

現(xiàn)在漏设,讓我們來設(shè)計星巴克咖啡

  1. 設(shè)計裝飾者調(diào)料抽象類Condiment
  2. 繼承被裝飾者Beverage
  3. 關(guān)聯(lián)Beverage
  4. 實現(xiàn)具體裝飾者類(牛奶、摩卡.....),并添加自己的行為
abstract class Condiment extends Beverage{
    protected Beverage beverage;
    public Condiment(Beverage beverage) {
        super("調(diào)料");
        this.beverage = beverage;
    }
}
class Milk extends Condiment {
    public Milk(Beverage beverage) {
        super(beverage);
    }
    @Override
    public double cost() {
        return beverage.cost() + 2;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + "牛奶";
    }
}

class Mocha extends Condiment {
    public Mocha(Beverage beverage) {
        super(beverage);
    }
    @Override
    public double cost() {
        return beverage.cost() + 4;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + "摩卡";
    }
}

最終UML類圖為


星巴克咖啡.png

客戶端測試

public static void main(String[] args) {
        Beverage b = new HouseBlend();
        Beverage b2 = new Milk(b);//加牛奶   
        Beverage b3 = new Mocha(b2);//加摩卡       
        Beverage b4 = new Mocha(b3);//加摩卡
        System.out.println(b4.getDescription() + ":" + b4.cost());
    }

輸出結(jié)果

混合咖啡牛奶摩卡摩卡:45.0

4今妄、裝飾者模式說明

優(yōu)點

  • 用組合可以實現(xiàn)運行時動態(tài)擴展郑口,比繼承的編譯時靜態(tài)決定要靈活
  • 當(dāng)增加新的裝飾時鸳碧,可以不改動現(xiàn)有的代碼,符合開閉原則

缺點

  • 引入裝飾者會增加大量的小類犬性,導(dǎo)致設(shè)計不容易被理解

其他

  • 裝飾者Codiment與被裝飾者Beverage并不是is-a的關(guān)系瞻离,這里用繼承的目的是為了達到類型匹配,而不是利用繼承獲得行為乒裆,行為是通過組合進行動態(tài)添加的套利。

5、Android中的裝飾者模式

5.1 Context

Context在Android開發(fā)中表示“上下文”鹤耍。Context類是一個抽象類肉迫,具體實現(xiàn)在ContexImpl中。繼承關(guān)系如下


Context繼承結(jié)構(gòu).png
  • 其中ContextWarpper繼承Context并持有Context的引用稿黄,這里對應(yīng)模式中的裝飾者喊衫,Context為被裝飾者。
public class ContextWrapper extends Context {
    Context mBase;
    public ContextWrapper(Context base) {
        mBase = base;
    }
    //........
}
  • Application杆怕、Service族购、Activity為具體裝飾類。
5.2 Drawable

Drawable也是Android開發(fā)中經(jīng)常用到的一個概念陵珍。是一個”可繪制東西“的抽象寝杖。用來做繪制相關(guān)的操作。跟View不同樣互纯,Drawable 不能接受任何事件以及用戶交互瑟幕。


Drawable繼承關(guān)系.png
  • DrawableWrapper為抽象裝飾者,繼承Drawable的同時持有Drawable類型的引用伟姐。
  • ShapeDrawable收苏、BitmapDrawable、LayerDrawable都是我們開發(fā)中用過具體實現(xiàn)類愤兵,只不過我們一般通過XML方式來定義鹿霸。
  • ClipDrawable、InsertDrawable秆乳、RotateDrawable懦鼠、ScaleDrawable為具體裝飾者∫傺撸可實現(xiàn)一些額外的裝飾效果肛冶。比如ClipDrwable可實現(xiàn)自身裁剪復(fù)制顯示功能。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扯键,一起剝皮案震驚了整個濱河市睦袖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荣刑,老刑警劉巖馅笙,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伦乔,死亡現(xiàn)場離奇詭異,居然都是意外死亡董习,警方通過查閱死者的電腦和手機烈和,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來皿淋,“玉大人招刹,你說我怎么就攤上這事∥讶ぃ” “怎么了疯暑?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長高帖。 經(jīng)常有香客問我缰儿,道長,這世上最難降的妖魔是什么散址? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任乖阵,我火速辦了婚禮,結(jié)果婚禮上预麸,老公的妹妹穿的比我還像新娘瞪浸。我一直安慰自己,他們只是感情好吏祸,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布对蒲。 她就那樣靜靜地躺著,像睡著了一般贡翘。 火紅的嫁衣襯著肌膚如雪蹈矮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天鸣驱,我揣著相機與錄音泛鸟,去河邊找鬼。 笑死踊东,一個胖子當(dāng)著我的面吹牛北滥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闸翅,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼再芋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坚冀?” 一聲冷哼從身側(cè)響起济赎,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后司训,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體华蜒,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年豁遭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贺拣。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蓖谢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出譬涡,到底是詐尸還是另有隱情闪幽,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布涡匀,位于F島的核電站盯腌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏陨瘩。R本人自食惡果不足惜腕够,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舌劳。 院中可真熱鬧帚湘,春花似錦、人聲如沸甚淡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贯卦。三九已至资柔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撵割,已是汗流浹背贿堰。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留睁枕,地道東北人官边。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像外遇,于是被迫代替她去往敵國和親注簿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354