Objective-C設計模式解析-外觀

看圖識模式

自助遊(圖1)算法

clipboard.png

旅行社(圖2)編程

clipboard.png

舉個栗子

不知道你們有沒有經過旅行社報團出去旅遊的經歷?這是一個很好的外觀模式的應用。設計模式

方式一:客戶直接調用各個子系統的功能,和各個子系統之間造成緊耦合的關係(上圖一)
方式二:提供一個高層接口,該高層接口負責和子系統進行交互,並向客戶提供須要使用的接口(上圖二)
從上面兩種方式的圖式結構能夠看到,對客戶來講,方式二比方式一要好用不少,由於在方式二中,客戶不須要知道各個子系統的邏輯,只須要和高層接口交互就能夠了。實際上方式二,就是咱們這裏要說的外觀模式了。架構

若是這裏不該用外觀模式,咱們(上圖中的Client),就得本身去聯繫交通工具、預約旅館、飯館、景點門票等,相信這樣的旅程,你們會感受很累。有了外觀角色(上圖中的Facade),它會幫咱們去處理這些事情。工具

迪米特法則

簡介

得墨忒耳定律(Law of Demeter,縮寫LoD)亦稱為「最少知識原則(Principle of Least Knowledge)」,是一種軟件開發的設計指導原則,特別是面向對象的程序設計。得墨忒耳定律是鬆耦合的一種具體案例。該原則是美國東北大學在1987年底在發明的。
這個原理的名稱來源於希臘神話中的農業女神,孤獨的得墨忒耳(Demeter)。spa

定義:一個對象應該對其餘對象保持最少的瞭解。
問題由來:類與類之間的關係越密切,耦合度越大,當一個類發生改變時,對另外一個類的影響也越大。架構設計

解決方案:儘可能下降類與類之間的耦合。設計

自從咱們接觸編程開始,就知道了軟件編程的總的原則:低耦合,高內聚。不管是面向過程編程仍是面向對象編程,只有使各個模塊之間的耦合儘可能的低,才能提升代碼的複用率。低耦合的優勢不言而喻,可是怎麼樣編程才能作到低耦合呢?那正是迪米特法則要去完成的。
迪米特法則又叫最少知道原則,最先是在1987年由美國Northeastern University的Ian Holland提出。通俗的來說,就是一個類對本身依賴的類知道的越少越好。也就是說,對於被依賴的類來講,不管邏輯多麼複雜,都儘可能地的將邏輯封裝在類的內部,對外除了提供的public方法,不對外泄漏任何信息。迪米特法則還有一個更簡單的定義:只與直接的朋友通訊。首先來解釋一下什麼是直接的朋友:每一個對象都會與其餘對象有耦合關係,只要兩個對象之間有耦合關係,咱們就說這兩個對象之間是朋友關係。耦合的方式不少,依賴、關聯、組合、聚合等。其中,咱們稱出現成員變量、方法參數、方法返回值中的類爲直接的朋友,而出如今局部變量中的類則不是直接的朋友。也就是說,陌生的類最好不要做爲局部變量的形式出如今類的內部。code

一些比喻

最多見的比喻是:不要和陌生人說話對象

看看這個:假設我在便利店購物。付款時,我是應該將錢包交給收銀員,讓她打開並取出錢?仍是我直接將錢遞給她?

再作一個比喻:人能夠命令一條狗行走(walk),可是不該該直接指揮狗的腿行走,應該由狗去指揮控制它的腿如何行走。

模式與意義

迪米特法則能夠簡單說成:talk only to your immediate friends。 對於OOD來講,又被解釋爲下面幾種方式:一個軟件實體應當儘量少的與其餘實體發生相互做用。每個軟件單位對其餘的單位都只有最少的知識,並且侷限於那些與本單位密切相關的軟件單位。
迪米特法則的初衷在於下降類之間的耦合。因爲每一個類儘可能減小對其餘類的依賴,所以,很容易使得系統的功能模塊功能獨立,相互之間不存在(或不多有)依賴關係。
迪米特法則不但願類之間創建直接的聯繫。若是真的有須要創建聯繫,也但願能經過它的友元類來轉達。所以,應用迪米特法則有可能形成的一個後果就是:系統中存在大量的中介類,這些類之因此存在徹底是爲了傳遞類之間的相互調用關係——這在必定程度上增長了系統的複雜度。
設計模式中的門面模式(Facade)和中介模式(Mediator),都是迪米特法則應用的例子。
值得一提的是,雖然Ian Holland對計算機科學的貢獻也僅限於這一條法則,其餘方面的建樹很少,可是,這一法則卻不只僅侷限於計算機領域,在其餘領域也一樣適用。好比,美國人就在航天系統的設計中採用這一法則。

模式定義

外觀模式爲子系統中一組不一樣的接口提供統一的接口。外觀定義了上層接口,經過下降複雜度和隱藏子系統間的通訊及依存關係,讓子系統易於使用。

API 的使用者徹底不知道這內部的業務邏輯有多麼複雜。當咱們有大量的類而且它們使用起來很複雜並且也很難理解的時候,外觀模式是一個十分理想的選擇。
外觀模式把使用和背後的實現邏輯成功解耦,同時也下降了外部代碼對內部工做的依賴程度。若是底層的類發生了改變,外觀的接口並不須要作修改。

結構圖

clipboard.png

這個定義,經過上面引言的圖示講解,應該很好理解了,這裏再分析一下定義中的兩個重要角色:

外觀角色:就是引言圖示中的「高層接口」,客戶端能夠調用這個角色的方法;另外,該角色知道相關的子系統的功能和責任。

子系統角色:能夠同時有一個或者多個子系統。每個子系統均可以被客戶端直接調用,或者被外觀角色調用。

代碼

比方說今天我不想開車,因而打電話叫了出租車。只要出租車能把我送到目的地,我不在意車牌和型號。我會直接對司機說「我要去somePlace」,而後司機會執行一系列的命令(鬆手剎、換擋、踩油門等等)。出租車司機抽象出了駕駛汽車的底層負責操做的細節。他經過提供駕駛服務(簡化了接口),把我與本來複雜的車輛操做接口分離。出租車和我直接的接口只是一個簡單的「我要去xxx」命名。

不少舊的面向對象應用程序中,可能有許多類分散於帶有各類功能的系統中。要把這些類用於某個功能,須要知道所有細節才能在一組算法中使用它們。若是從邏輯上將其中一些類組合成一個簡單的接口,可讓這些類更易於使用。

clipboard.png

編寫代碼

Car定義了幾個操其內部對象的方法,如pressBrakes、releaseBrakes、changGears...。客戶端想要使用Car的內部功能,必須瞭解如何使用這些方法進行正確操做。

@interface Car : NSObject

/// 踩剎車
- (void)pressBrakes;
/// 鬆剎車
- (void)releaseBrakes;
/// 換擋
- (void)changGears;
/// 踩油門
- (void)pressAccelerator;
/// 鬆油門
- (void)releaseAccelerator;

@end 


@implementation Car

- (void)pressBrakes
{
    NSLog(@"car: 踩剎車");
}

- (void)releaseBrakes
{
    NSLog(@"car: 鬆剎車");
}

- (void)changGears
{
    NSLog(@"car: 換擋");
}

- (void)pressAccelerator
{
    NSLog(@"car: 踩油門");
}

- (void)releaseAccelerator
{
    NSLog(@"car: 鬆油門");
}

@end

Taximeter自己是一個複雜的系統,它有兩個讓客戶端操做其對象的方法。start和stop方法只是讓Taximeter開始或中止。這裏不深刻Taximeter的細節。

@interface Taximeter : NSObject

/// 開始打表
- (void)start;
/// 結束打表
- (void)stop;

@end  


@implementation Taximeter

- (void)start
{
    NSLog(@"Taximeter: 開始打表");
}

- (void)stop
{
    NSLog(@"Taximeter: 結束打表");
}

@end

目前,出租車服務系統裏面有兩個複雜的子系統。須要一個CarDriver做爲外觀以簡化接口。代碼以下

@interface CarDriver : NSObject

- (void)driveToLocation:(CGPoint)place;

@end  


@implementation CarDriver

- (void)driveToLocation:(CGPoint)place
{
    // ....
    
    // 開啓計價器
    Taximeter *meter = [Taximeter new];
    [meter start];
    
    // 操做車輛,直到到達位置place
    Car *car = [Car new];
    [car releaseBrakes];
    [car changGears];
    [car pressAccelerator];
    
    // ....
    
    // 到達位置place,停下車和計價器
    [car releaseAccelerator];
    [car pressBrakes];
    [meter stop];
    
    // ....
}

@end

CarDriver的外觀方法決定了客戶端能夠用多簡單的方式使用整個出租車服務系統。客戶只需調用driveToLocation:方法,其他的操做就會在消息調用中發生。客戶端不須要了解底層發生的一切。

總結

經過上面的講解,咱們來分析一下外觀模式的特色:

  • Facade設計模式注重從架構的層次去看整個系統,而不是單個類的層次。不少時候,它是一種架構設計模式,好比咱們經常使用的三層架構。

  • Facade模式簡化了整個系統的接口,同時對於系統內部(子系統)與外部程序(Client)來講,也達到了一種「解耦」的效果。

根據外觀模式的特色,咱們能夠在如下狀況中使用Facade模式:

  • 不須要使用一個複雜系統的全部功能,只須要使用系統的部分功能時,那麼應用Façade模式提供一個高層接口將比直接調用原系統的接口簡單得多。

  • 但願封裝或者隱藏原系統的接口時,能夠考慮使用外觀模式。

  • 但願使用原系統的部分功能,並且還但願增長一些新的功能。

  • 構建一個具備層次結構的子系統時,使用Facade模式定義子系統中每層的高級接口。若是子系統之間是相互依賴的,你可讓它們僅經過Facade進行通信,從而簡化了它們之間的依賴關係。

其實這個設計模式咱們很常見,通常咱們使用第三方類的時候都會有這種模式,使用第三方時咱們只須要引用第三方的其中改一個文件就能知足不少功能的使用。我只這個文件就是講子系統的一些方法歸併到了這個文件中,從而使使用者上手更快。

相關文章
相關標籤/搜索