裝飾/原型/外觀設計模式簡單理解

根本停不下來發表於2018-08-10

裝飾模式:

給原有的物件新增新的功能,但不繼承原有的類。
Cocoa 中 Category 就屬於這種設計模式。
舉例:實現一個執行緒安全的陣列

模板/原型模式:

某個物件,它的構成/建立十分複雜,可以考慮這種模式。
Cocoa 中 NSCoping 就屬於這種模式。
舉例: 假設有一個類 Model,它裡面有 100 個屬性 property1 ~ property100。有個物件 model1,這些屬性都已經複製。現在如果需要一個 model2 和 model3 ,它們都與 model1 只有一個屬性的值是不同的,那麼有一種做法就是 把生成 model1 的 101 行程式碼複製貼上兩次再修改一下。 還有一種做法,在 Model 類中加一個方法:- (Model *)clone;,然後實現 clone:

- (Model *)clone {
    Model *model = [[self class] init];
    model.property1 = self.property1;
    ...
    model.property100 = self.property100;
    return model;
}
複製程式碼

這樣我們建立其它 model 的時候就可以這樣做:

Model *model2 = [model1 clone];
model2.propertyN = xxx;
複製程式碼

NSCopying 就是這樣的。NSCopying 協議使用的時候要注意以下問題,假設 Person 類是這樣的:

裝飾/原型/外觀設計模式簡單理解
copyWithZone:的實現:
裝飾/原型/外觀設計模式簡單理解
這樣就會出現一個問題,如下所示:

裝飾/原型/外觀設計模式簡單理解
person 的 sons 和 person2 的 sons 完全是同一個物件,假設 sons 可以修改的話,那麼修改 person.sons 和 person2.sons 會相互影響。這完全不是我們想要的結果了。我們可以這樣做:

裝飾/原型/外觀設計模式簡單理解

修改之後,看下面這段程式碼的輸出:

裝飾/原型/外觀設計模式簡單理解

person.sons 與 person2.sons 是兩個不同的陣列了。但是裡面的元素卻是相同的。這裡就牽涉到幾個比較繞的概念了:

  • 淺拷貝
  • 單層拷貝/單層深拷貝/深拷貝
  • 完全拷貝(可以用歸檔來處理

感興趣的話可以自己搜一下。現在我們這種情況屬於第二種。

外觀/Facade模式(可以解耦)

先舉個例子:假設我們要從西安去北京,可以選擇自駕/坐飛機/坐火車等方式,都可以去,可以理解為通過不同的手段達到同一個目的,但是細節不同,但是我們不關心它的細節,只關心它的目的。

外觀模式使用場景:
有一個複雜的子系統,它為了得到某種結果,裡面有很多複雜的邏輯,客戶端只想要這個結果,並不關心裡面複雜的邏輯。可以寫一箇中間類,操作這個子系統,客戶端只用從中間類來獲取結果即可。

外觀模式的好處:
解耦合
簡化操作

外觀模式與介面卡模式的區別:
外觀模式 -- 意圖提供簡單的介面
介面卡模式 -- 轉換介面

舉例:
假設我們有一個 Shape 類,和他的一些子類們:

@interface Shape : NSObject

- (void)draw;

@end
****************************************
@interface Rectangle : Shape

@property (assign, nonatomic) CGFloat width;
@property (assign, nonatomic) CGFloat height;

@end

@implementation Rectangle

- (void)draw
{
    繪製一個矩形;
}

@end
****************************************
@interface Circle : Shape

@property (assign, nonatomic) CGFloat radius;

@end

@implementation Circle

- (void)draw
{
    繪製一個圓形;
}

@end
複製程式碼

假設我們在 ViewController 中寫了如下程式碼:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //繪製一個矩形
    Rectangle *tangle = [[Rectangle alloc] init];
    tangle.width = 10;
    tangle.height = 20;
    [tangle draw];
    
    //繪製一個圓形
    Circle *cir = [[Circle alloc] init];
    cir.radius = 5;
    [cir draw];
}
複製程式碼

如果 Shape 有多個子類,比如平行四邊形、梯形、八邊形等。那麼它會是一個特別複雜的系統,並且我們還需要知道這個系統的細節,把資料具體地傳遞到每個物件對應的屬性上才可以。

但其實上我們並不關心在這個系統中它們是怎麼實現的,我們就只想讓它畫個圖形就 OK 了。這是可以這麼做,建立一箇中間類 ShapeMaker:

@interface ShapeMaker : Shape

- (void)drawRectangleWithParams:(NSDictionary *)dic;
- (void)drawCircleWithParams:(NSDictionary *)dic;
- (void)drawRectangleAndCircleWithParams:(NSDictionary *)dic;
...

@end
複製程式碼

在 ShapeMaker 中去實現這些複雜的操作,ShapeMaker 提供一些簡單的介面供 ViewController 使用,ViewController 只用使用介面,傳遞資料即可,ViewController 並不關心具體實現,也不知道具體每個資料用在哪個地方,它只是讓 ShapeMaker 畫個形狀就可以了。

相關文章