在面向對象編程中有個重要的原則,里氏代換原則:一個軟件實體如果使用的是一個父類的話,那么一定適用其子類,而且它察覺不出父類對象與子類對象的區別。也就是說,在軟件設計里面,把父類替換成它的子類,程序的行為沒有變化。簡單的說,子類類型必須能替換掉它的父類類型。
就好像繼承的概念,子類繼承自父類,那么子類可以以父類的身份出現。有這樣一個問題,在面向對象設計中,一個是鳥類,一個是企鵝類,如果鳥是可以飛得,企鵝不會飛,那么企鵝是鳥么?企鵝可以繼承自鳥類么?
需要面向對象設計,那么意味着,子類擁有父類所以非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武裝到牙齒,也可以只讓他穿條內褲。
裝飾模式的優點:
- 把類中的裝飾功能從類中搬移出去,這樣可以簡化原有的類。
- 當有效的把類中的核心功能和裝飾功能區分開了,可以去除相關類中重復的裝飾邏輯。




