每一個早晨出門前都要穿衣打扮,根據參加的場所選擇不一樣的服飾。
好比如今有若干衣服:運動鞋、運動褲、衛衣、襯衫、西服、皮鞋、內衣等。
提出需求: 這周分別參加公益酒會、運動會、cosplay三個活動。怎麼搭配這些衣服了,設計成類如何實現?編程
能夠這樣搭配,如圖性能
生成3個子類分別繼承Person類:學習
看起來很好,並且解決了問題。可是發現一樣也有一些問題,ui
在一個子類裏定義所有的功能,須要穿哪件衣服(哪一個功能)就穿哪件衣服(調用對應功能),可是這不知足單一職責
原則(不一樣的功能不該該放在同一個類裏面),並且這個類會過於臃腫而沒法維護,而且大部分功能是使用不到的,只有在相應的場景纔會須要。
並且若是增長新功能也要改變這裏類,也不符合開放-封閉
原則。atom
經過上面的例子咱們能夠發現2個規律spa
第一: 穿的衣服是能夠任意組合的,理論上穿幾件、穿哪一種類型均可以。設計
也就是動態添加
第二: 穿完一件衣服能夠再穿另外一件衣服,穿完一件衣服後我仍是我(類型沒發生變化)。穿一件衣服以前不用關心我穿沒穿衣服、穿了幾件衣服,穿完以後一樣也是。調試
添加以後類型不發生變化,添加先後均可以被一致對待。也就是裝飾前和裝飾後 沒有什麼不一樣
爲了實現這些目的,能夠這樣設計:code
首先,聲明一個抽象接口Person,它有一個show方法來展現當前的穿着打扮。具體的人(Person)實現這個接口好比黃種人(YellowMan),show方法只輸出人名,在未裝飾以前就只是一個單純的人。
而後再定義一個裝飾類Decorator,它也實現接口Person,但不一樣的是它擁有一個具體對象(YellowMan)的引用,並且多了一個addBehavior方法,這個方法裏實現對具體對象的裝飾(添加職責)。
最後建立具體的Decorator類,實現具體的addBehavior方法。
把一個具體的人類(Person)傳遞建立一個具體的裝飾類,因爲裝飾類(Decorator)和人類(Person)擁有相同的接口,因此它倆的對外使用是一致的。當調用show的時候,經過對具體人類(Person)的引用調用它對應的show方法,同時調用裝飾方法(addBehavior),達到了添加職責的目的。對象
看一下設計類圖:
這樣咱們就能夠把任意的裝飾類鏈接起來使用,用圖表示應該是這樣的
有幾件衣服(職責),就建立幾個裝飾類,具體怎麼穿就能夠隨意搭配了。下面看一下代碼怎麼寫?
抽象接口Person
@protocol Person <NSObject> - (void)show; @end
具體人YellowMan實現這個接口Person
#import "Person.h" @interface YellowMan : NSObject <Person> - (instancetype)initWithName:(NSString *)name; - (void)show; @end @interface YellowMan () @property (nonatomic, copy) NSString *name; @end @implementation YellowMan - (instancetype)initWithName:(NSString *)name { self = [super init]; if (self) { _name = name; } return self; } - (void)show { NSLog(@"我是: %@", self.name); } @end
定義一個裝飾類Decorator
@interface Decorator : NSObject <Person> - (instancetype)initWithPerson:(id <Person>)person; - (void)show; @end @interface Decorator () @property (nonatomic, strong) id <Person>person; @end @implementation Decorator - (instancetype)initWithPerson:(id <Person>)person { self = [super init]; if (self) { _person = person; } return self; } - (void)show { [self.person show]; } @end
定義具體的裝飾類: 襯衫ShirtDecorator
@interface ShirtDecorator : Decorator @end @implementation ShirtDecorator - (void)show { [super show]; [self addBehavior]; } - (void)addBehavior { NSLog(@"-- 穿襯衫"); } @end
定義具體的裝飾類: 西裝SuitDecorator
@interface SuitDecorator : Decorator @end @implementation SuitDecorator - (void)show { [super show]; [self addBehavior]; } - (void)addBehavior { NSLog(@"-- 穿西裝"); } @end
其它的裝飾類都相似,再也不一一寫了;
client調用
YellowMan *aMan = [[YellowMan alloc] initWithName:@"小明"]; ShirtDecorator *shirtA = [[ShirtDecorator alloc] initWithPerson:aMan]; SuitDecorator *suitA = [[SuitDecorator alloc] initWithPerson:shirtA]; [suitA show]; // 小李是超人,內衣穿外面 YellowMan *bMan = [[YellowMan alloc] initWithName:@"小李"]; ShirtDecorator *shirtB = [[ShirtDecorator alloc] initWithPerson:bMan]; SuitDecorator *suitB = [[SuitDecorator alloc] initWithPerson:shirtB]; UnderwearDecorator *underwear = [[UnderwearDecorator alloc] initWithPerson:suitB]; [underwear show];
運行結果:
咱們發現被裝飾過的對象任然和沒裝飾前的同樣,它的功能沒有發生改變,只是多了被裝飾的功能,使用方式也沒有發生變化。
並且被裝飾後的對象還能夠被繼續裝飾,裝飾多少次和裝飾順序徹底能夠動態控制。
裝飾模式: 動態地給一個對象添加一些額外的職責。就拓展功能來講,裝飾模式相比生成子類更爲靈活。
裝飾模式包含以下角色:
經過上面的結構咱們發現:
類應該對擴展開放,對修改關閉
裝飾模式能夠提供比繼承更多的靈活性。裝飾模式容許系統動態決定「貼上」一個須要的「裝飾」,或者除掉一個不須要的「裝飾」。繼承關係則不一樣,繼承關係是靜態的,它在系統運行前就決定了。
在如下狀況下可使用裝飾模式:
裝飾模式可分爲透明裝飾模式和半透明裝飾模式:在透明裝飾模式中,要求客戶端徹底針對抽象編程,裝飾模式的透明性要求客戶端程序不該該聲明具體構件類型和具體裝飾類型,而應該所有聲明爲抽象構件類型;
例如:
// 應該這樣 id <Person>person = [[YellowMan alloc] initWithName:@"小明"]; id <Person>shirtPerson = [[ShirtDecorator alloc] initWithPerson:person]; id <Person>suitPerson = [[SuitDecorator alloc] initWithPerson:shirtPerson]; [suitPerson show]; // 不該該這樣 YellowMan *aMan = [[YellowMan alloc] initWithName:@"小明"]; ShirtDecorator *shirtA = [[ShirtDecorator alloc] initWithPerson:aMan]; SuitDecorator *suitA = [[SuitDecorator alloc] initWithPerson:shirtA]; [suitA show];
半透明裝飾模式允 許用戶在客戶端聲明具體裝飾者類型的對象,調用在具體裝飾者中新增的方法。
然而,純粹的裝飾模式很難找到。裝飾模式的用意是在不改變接口的前提下,加強所考慮的類的性能。在加強性能的時候,每每須要創建新的公開的方法。這就致使了大多數的裝飾模式的實現都是「半透明」的,而不是徹底透明的。換言之,容許裝飾模式改變接口,增長新的方法。這意味着客戶端能夠聲明ConcreteDecorator類型的變量,從而能夠調用ConcreteDecorator類中才有的方法。
根據Objective-C的特性,有兩種實現方式:
第二種方式是使用了Objective-C的語言功能,經過分類向類添加行爲,沒必要進行子類化,這並不是標準的裝飾模式結構,可是實現了裝飾模式一樣的需求。儘管使用分類來實現裝飾模式跟原始風格有偏離,可是實現少許的裝飾器的時候,它比真正子類方式更加輕量、更加容易。