標籤:
在物件導向編程中有個重要的原則,裡氏代換原則:一個軟體實體如果使用的是一個父類的話,那麼一定適用其子類,而且它察覺不出父類對象與子類對象的區別。也就是說,在軟體設計裡面,把父類替換成它的子類,程式的行為沒有變化。簡單的說,子類類型必須能替換掉它的父類類型。
就好像繼承的概念,子類繼承自父類,那麼子類可以以父類的身份出現。有這樣一個問題,在物件導向設計中,一個是鳥類,一個是企鵝類,如果鳥是可以飛得,企鵝不會飛,那麼企鵝是鳥嗎?企鵝可以繼承自鳥類嗎?
需要物件導向設計,那麼意味著,子類擁有父類所以非private的屬性和行為,鳥會飛,而企鵝不會,所以企鵝是鳥,但它不能繼承自上面那個會飛的鳥類,抽象出一個更高的鳥類,然後分為會飛的鳥子類、不會飛的鳥子類,企鵝應該繼承自不會飛的鳥子類。
這因為有了裡氏代換原則,才使得繼承複用成為可能,只有當子類可以替換掉父類,軟體單位的功能不受到影響時,父類才能真正被複用,而子類也可以在父類的基礎上增加新的行為。正是由於子類的可替換性才使得父類類型模組在無需修改的情況下就能擴充,這是前面提到的,對擴充的開放,對修改的封閉(ocp原則)。
裝飾模式
穿衣問題,要求寫一個給人搭配不同服飾的系統,那種可以換各種各樣衣服和褲子的服飾系統,如:
首先排除這樣的結果設計,如果我需要新增加超人的服飾設計,又得更改Person類,很明顯違背了開發-封閉原則(ocp,對擴充的開放,對修改的封閉)。其實把這些服飾類寫成子類就好,代碼結構:
如此,需要增加超人的裝扮 ,只需要增加子類即可。不需要對已有的代碼進行修改。但是這樣還打不到最好,我們需要在控制器裡面來開闢諸如"破球鞋"、“垮褲”等對象,將他們一個詞一個詞的顯示出來,就好比是在眾目睽睽下穿衣服。
對於這些,應當去最佳化它們。就可以用到裝飾模式:動態給一個對象添加一些額外的職能,就增加功能來說,裝飾模式比添加子類更加靈活。無論是衣服、鞋子、褲子等,其實我們都可以把它理解為對Person的裝飾,那麼有結構:
代碼:
Person類:
#import <Foundation/Foundation.h>@interface ZYPerson : NSObject{ @protected NSString *_name;}- (instancetype)initWithName:(NSString *)name;- (void)display;@end#import "ZYPerson.h"@implementation ZYPerson- (instancetype)initWithName:(NSString *)name{ if (self = [super init]) { _name = name; } return self;}- (void)display{ NSLog(@"裝扮的%@:",_name);}@end
clothing類:
#import "ZYPerson.h"@interface ZYClothing : ZYPerson@property (nonatomic, strong) ZYPerson *decorate;- (instancetype)initWithDecorate:(ZYPerson *)decorate;@end#import "ZYClothing.h"@implementation ZYClothing- (instancetype)initWithDecorate:(ZYPerson *)decorate{ if (self = [super init]) { _decorate = decorate; } return self;}- (void)display{ if (self.decorate) { [self.decorate display]; }}@end
TShirts類:
#import "ZYClothing.h"@interface ZYTShirts : ZYClothing@end#import "ZYTShirts.h"@implementation ZYTShirts- (void)display{ [super display]; NSLog(@"大襯衫");}@end
Pants類:
#import "ZYClothing.h"@interface ZYPants : ZYClothing@end#import "ZYPants.h"@implementation ZYPants- (void)display{ [super display]; NSLog(@"大褲衩");}@end
Shoe類:
#import "ZYClothing.h"@interface ZYShoe : ZYClothing@end#import "ZYShoe.h"@implementation ZYShoe- (void)display{ [super display]; NSLog(@"破鞋子");}@end
viewController裡面的代碼:
#import "ViewController.h"#import "ZYPerson.h"#import "ZYClothing.h"#import "ZYTShirts.h"#import "ZYPants.h"#import "ZYShoe.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. ZYPerson *person = [[ZYPerson alloc] initWithName:@"晶童鞋"]; ZYClothing *clothing = [[ZYClothing alloc] initWithDecorate:person]; ZYTShirts *shirts = [[ZYTShirts alloc] init]; ZYPants *pants = [[ZYPants alloc] init]; ZYShoe *shoe = [[ZYShoe alloc] init]; //裝扮過程,相當於在室內穿衣服,控制器並不知道它是怎麼的順序 shirts.decorate = clothing; pants.decorate = shirts; shoe.decorate = pants; [shoe display]; //第二次裝扮 pants.decorate = clothing; shoe.decorate = pants; shirts.decorate = shoe; [shirts display];}@end
運行:
裝飾模式總結:
我覺得裝飾模式,是為已有功能動態添加更多功能的一種方法。但是到底什麼時候用它呢?
在本文的最初設計中,當系統需要添加新功能的時候,是向舊的類中添加新的代碼,這些新增的代碼通常裝飾了原有類的核心職能或主要行為。這種設計方式問題在於,他們在主類中增加了新的欄位、新的方法、新的邏輯,從而增加了主類的負責度。而這些新加入的東西僅僅是為了滿足一些在某種特定情況下才會執行的特殊行為的需求。
而裝飾模式提供了一個非常好的解決方案,它把每個要裝飾的功能放在單獨的類中,並讓這個類包含它所要裝飾的對象,因此,當執行特殊行為時,在viewController裡就可以根據需求有選擇、按順序的使用裝飾功能封裝對象了。
所以就有了上面的代碼,我可以通過裝飾,讓person武裝到牙齒,也可以只讓他穿條內褲。
裝飾模式的優點:
- 把類中的裝飾功能從類中搬移出去,這樣可以簡化原有的類。
- 當有效把類中的核心功能和裝飾功能區分開了,可以去除相關類中重複的裝飾邏輯。
設計模式之裝飾模式(iOS開發,代碼用Objective-C展示)