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的英雄赞别,有哪些方式呢?
- 不改變抽象類的情況下配乓,最容易想到的方式仿滔,自然是采取繼承的方式。但是如果為每個(gè)英雄加持不同buff都新建一個(gè)類的話犹芹,這樣無疑會(huì)照成類爆炸性增長(zhǎng)崎页,畢竟英雄數(shù)現(xiàn)在就有上百個(gè)了,未來還一直在增加腰埂。
- 使用裝飾器模式為其動(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í)候并不一定所有的角色都要有羔挡,一些特定情況可以省略一些角色洁奈。
-
當(dāng)只有一個(gè)具體構(gòu)件(ConcreteComponent)時(shí)间唉,可以省略抽象構(gòu)件(Component)绞灼,UML類圖如下:
-
當(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ī)等