iOS設(shè)計(jì)模式-裝飾器模式

1. 什么是裝飾器設(shè)計(jì)模式

裝飾模式(Decorator Pattern 定義

Attach additionalresponsibilities to an object dynamically keeping the same interface.Decorators provide aflexible alternative to subclassing for extending functionality.(動(dòng)態(tài)地給一個(gè)對(duì)象添加一些額外的職責(zé)备典。就增加功能來說缔杉,裝飾模式相比生成子類更為靈活。)

是指在不改變現(xiàn)有對(duì)象結(jié)構(gòu)的情況下叠穆,動(dòng)態(tài)地給該對(duì)象增加一些職責(zé)(即增加其額外功能)的模式

2. 角色組成

裝飾模式主要包含以下角色:

  • 抽象構(gòu)件(Component):定義一個(gè)抽象接口以規(guī)范準(zhǔn)備接收附加責(zé)任的對(duì)象雌团。
  • 具體構(gòu)件(ConcreteComponent):實(shí)現(xiàn)抽象構(gòu)件,通過裝飾角色為其添加一些職責(zé)。
  • 抽象裝飾(Decorator):繼承抽象構(gòu)件拷恨,并包含具體構(gòu)件的實(shí)例,可以通過其子類擴(kuò)展具體構(gòu)件的功能谢肾。
  • 具體裝飾(ConcreteDecorator):實(shí)現(xiàn)抽象裝飾的相關(guān)方法腕侄,并給具體構(gòu)件對(duì)象添加附加的責(zé)任。

UML關(guān)系圖:


裝飾器模式

3. 完全透明與半透明模式

  • 完全透明模式:裝飾模式對(duì)客戶端的透明性要求程序不要聲明給一個(gè)ConcreteComponent類型的變量芦疏,而應(yīng)當(dāng)聲明一個(gè)Component類型的變量冕杠。也就是說客戶端在調(diào)用時(shí),只能與Componnet實(shí)例來調(diào)用代碼酸茴,這就要求ConcreteDecorator的接口與Component保持一致分预,不去新增接口,只對(duì)原有的接口進(jìn)行增強(qiáng)薪捍。
  • 半透明模式:完全透明模式是比較少見的笼痹,因?yàn)樵谠鰪?qiáng)功能的過程中配喳,往往會(huì)新增接口。這就導(dǎo)致客戶端調(diào)用新接口的時(shí)候凳干,必須聲明ConcreteDecorator類型的變量晴裹,這樣才能調(diào)用新增的接口。這種有新增功能的情況稱為半透明模式救赐,也稱作半裝飾涧团、半適配器模式。

4. 代碼示例

我們一英雄聯(lián)盟英雄技能為例经磅。英雄聯(lián)盟作為一款競(jìng)技網(wǎng)游少欺,每個(gè)英雄(Hero)本身具有一個(gè)被動(dòng)技能buff,并且可以通過擊殺特定的中立野怪而獲取到對(duì)應(yīng)的buff在一定時(shí)間內(nèi)增加自身的屬性馋贤。為了實(shí)現(xiàn)被加持不同buff的英雄赞别,有哪些方式呢?

  1. 不改變抽象類的情況下配乓,最容易想到的方式仿滔,自然是采取繼承的方式。但是如果為每個(gè)英雄加持不同buff都新建一個(gè)類的話犹芹,這樣無疑會(huì)照成類爆炸性增長(zhǎng)崎页,畢竟英雄數(shù)現(xiàn)在就有上百個(gè)了,未來還一直在增加腰埂。
  2. 使用裝飾器模式為其動(dòng)態(tài)的新增或者移除buff飒焦。
    無疑在這種場(chǎng)景下,選擇裝飾器模式去實(shí)現(xiàn)是更加合理的屿笼。接下來對(duì)其進(jìn)行代碼實(shí)現(xiàn)牺荠。

先畫出UML類圖:


先定義抽象構(gòu)件類Hero,通過方法blessBuff展示英雄此時(shí)所加持的buff

// Hero.h
#import <Foundation/Foundation.h>

@interface Hero : NSObject
- (void)blessBuff;
@end

// Hero.m
#import "Hero.h"

@implementation Hero

- (void)blessBuff {
    NSAssert(false, @"must implement in subClass");
}

@end

定義英雄蓋倫Galen和Timo

// Galen.h
#import "Hero.h"

@interface Galen : Hero

@end

// Galen.m
#import "Galen.h"

@implementation Galen
- (void)blessBuff {
    NSLog(@"蓋倫被動(dòng)技能:脫離戰(zhàn)斗后回血加快");
}
@end

// Timo.h
#import "Hero.h"

@interface Timo : Hero

@end

// Timo.m
#import "Timo.h"

@implementation Timo
- (void)blessBuff {
    NSLog(@"提莫被動(dòng)技能:脫離戰(zhàn)斗后驴一,靜止不動(dòng)一段時(shí)間進(jìn)入隱身");
}
@end

定義buff抽象裝飾器BuffDecorator

#import "Hero.h"

// BuffDecorator.h
@interface BuffDecorator : Hero
- (instancetype)initWithHero:(Hero *)hero;
@end

//BuffDecorator.m
#import "BuffDecorator.h"

@interface BuffDecorator()
@property (nonatomic, strong) Hero *hero;
@end

@implementation BuffDecorator

- (instancetype)initWithHero:(Hero *)hero {
    self = [super init];
    if (self) {
        _hero = hero;
    }
    return self;
}

- (void)blessBuff {
    [_hero blessBuff];
    NSLog(@"額外buff:");
}

@end

先來個(gè)紅buff裝飾器

// RedBuffDecorator.h
#import "BuffDecorator.h"

@interface RedBuffDecorator : BuffDecorator

@end

// RedBuffDecorator.m
#import "RedBuffDecorator.h"

@implementation RedBuffDecorator

- (void)blessBuff {
    [super blessBuff];
    NSLog(@"紅buff: 攻擊附加真實(shí)傷害休雌,并造成灼燒效果");
}
@end

再來個(gè)藍(lán)buff裝飾器

// BlueBuffDecorator.h
#import "BuffDecorator.h"

@interface BlueBuffDecorator : BuffDecorator

@end

// BlueBuffDecorator.m
#import "BlueBuffDecorator.h"

@implementation BlueBuffDecorator
- (void)blessBuff {
    [super blessBuff];
    NSLog(@"藍(lán)buff: 藍(lán)量回復(fù)速度加快,并且縮減技能CD");
}
@end

開始調(diào)用肝断,全透明模式下杈曲,直接聲明變量類型為抽象構(gòu)件類,Hero即可胸懈,然后依次使用不同裝飾器為其增加不同buff

+ (void)test {
    NSLog(@"----------------Galen----------------------");
    Hero *galen = [Galen new];
    galen = [[RedBuffDecorator alloc] initWithHero:galen];
    [galen blessBuff];
    galen = [[BlueBuffDecorator alloc] initWithHero:galen];
    [galen blessBuff];
    NSLog(@"----------------Timo----------------------");
    Hero *timo = [Timo new];
    timo = [[RedBuffDecorator alloc] initWithHero:timo];
    [timo blessBuff];
    timo = [[BlueBuffDecorator alloc] initWithHero:timo];
    [timo blessBuff];
}

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


5. 分析

  • 采用裝飾器模式可以動(dòng)態(tài)的擴(kuò)展一個(gè)實(shí)現(xiàn)類的功能
  • 采用裝飾模式擴(kuò)展對(duì)象的功能比采用繼承方式更加靈活担扑。
  • 可以設(shè)計(jì)出多個(gè)不同的具體裝飾類,創(chuàng)造出多個(gè)不同行為的組合趣钱。
  • 裝飾模式增加了許多子類涌献,如果過度使用會(huì)使程序變得很復(fù)雜

6. 適用場(chǎng)景

  • 可以作為替代繼承的一種方式
  • 動(dòng)態(tài)地為對(duì)象拓展或撤銷功能,但是又不希望對(duì)整個(gè)類進(jìn)行拓展或撤銷相應(yīng)功能的情況
  • 為諸多兄弟類拓展功能時(shí)

7. 模式拓展

裝飾器模式在使用的時(shí)候并不一定所有的角色都要有羔挡,一些特定情況可以省略一些角色洁奈。

  1. 當(dāng)只有一個(gè)具體構(gòu)件(ConcreteComponent)時(shí)间唉,可以省略抽象構(gòu)件(Component)绞灼,UML類圖如下:


  2. 當(dāng)只有一個(gè)具體裝飾類(ConcreteDecorator)時(shí)利术,可以把抽象裝飾類和具體抽象類進(jìn)行合并.UML類圖如下:


8. 裝飾器模式和代理模式的區(qū)別

裝飾器模式可以看做是代理模式的一個(gè)特殊應(yīng)用,他們有的代碼實(shí)現(xiàn)看起來非常像低矮,尤其是在裝飾器模式省略了抽象裝飾器后印叁。我們來分析下他們的異同點(diǎn):

相同點(diǎn)

  • 對(duì)裝飾器模式來說,裝飾者和被裝飾者都實(shí)現(xiàn)同一個(gè) 接口军掂。對(duì)代理模式來說轮蜕,代理類和真實(shí)處理的類也都實(shí)現(xiàn)同一個(gè)接口。他們之間的邊界確實(shí)比較模糊蝗锥,兩者都是對(duì)類的方法進(jìn)行擴(kuò)展

異同點(diǎn)

  • 代理模式強(qiáng)調(diào)對(duì)訪問的限制跃洛,被代理對(duì)象往往不易直接獲取,或者希望得到隱藏终议。裝飾器模式更多的強(qiáng)調(diào)增強(qiáng)功能上汇竭,在原有的類上動(dòng)態(tài)地拓展功能
  • 代理模式是把當(dāng)前的行為或功能委托給其他對(duì)象執(zhí)行,代理類負(fù)責(zé)接口限定:是否可以調(diào)用真實(shí)角色穴张,以及是否對(duì)發(fā)送到真實(shí)角色的消息進(jìn)行變形處理细燎,它不對(duì)被主題角色(也就是被代理類)的功能做任何處理,保證原汁原味的調(diào)用
  • 裝飾模式是在要保證接口不變的情況下加強(qiáng)類的功能皂甘,它保證的是被修飾的對(duì)象功能比原始對(duì)象豐富(當(dāng)然玻驻,也可以減弱),但不做準(zhǔn)入條件判斷和準(zhǔn)入?yún)?shù)過濾偿枕,如是否可以執(zhí)行類的功能璧瞬,過濾輸入?yún)?shù)是否合規(guī)等
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市渐夸,隨后出現(xiàn)的幾起案子彪蓬,更是在濱河造成了極大的恐慌,老刑警劉巖捺萌,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件档冬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡桃纯,警方通過查閱死者的電腦和手機(jī)酷誓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來态坦,“玉大人盐数,你說我怎么就攤上這事∩√荩” “怎么了玫氢?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵帚屉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我漾峡,道長(zhǎng)攻旦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任生逸,我火速辦了婚禮牢屋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘槽袄。我一直安慰自己烙无,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布遍尺。 她就那樣靜靜地躺著截酷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乾戏。 梳的紋絲不亂的頭發(fā)上迂苛,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音歧蕉,去河邊找鬼灾部。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惯退,可吹牛的內(nèi)容都是我干的赌髓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼催跪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼锁蠕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起懊蒸,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤荣倾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后骑丸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舌仍,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年通危,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了铸豁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡菊碟,死狀恐怖节芥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤头镊,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布蚣驼,位于F島的核電站,受9級(jí)特大地震影響相艇,放射性物質(zhì)發(fā)生泄漏颖杏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一厂捞、第九天 我趴在偏房一處隱蔽的房頂上張望输玷。 院中可真熱鬧队丝,春花似錦靡馁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至膘盖,卻和暖如春胧弛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背侠畔。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工结缚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人软棺。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓红竭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親喘落。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茵宪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345