五、設計模式的六大設計原則之迪米特法則(LOD,Law Of Demeter)

1. 何為迪米特法則

狹義的迪米特法則定義:也叫最少知識原則(LKP,Least Knowledge Principle)。如果兩個類不必彼此直接通信,那么這兩個類就不應當發生直接的相互作用。如果其中的一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用。

廣義的迪米特法則定義:一個模塊設計得好壞的一個重要的標志就是該模塊在多大的程度上將自己的內部數據與實現有關的細節隱藏起來。信息的隱藏非常重要的原因在于,它可以使各個子系統之間解耦,從而允許它們獨立地被開發、優化、使用閱讀以及修改。

定義解讀:

迪米特法則通常被表述為:Only talk to your immediate friends ( 只和離你最近的朋友進行交互)。什么是最近的朋友?我們知道,每個對象都會與其他對象之間有耦合關系,只要兩個對象之間有耦合關系,我們就說這兩個對象之間是朋友關系。耦合的方式很多,如依賴、關聯、組合、聚合等。其中,我們稱出現在成員變量、方法參數、方法返回值中的類為最近的朋友,而出現在局部變量中的類則不是最近的朋友。迪米特法則是很好的解耦合手段之一。在網上看到的比較形象的說明這個法則的示例:

如果你想讓你的狗狗跑的話,你會對狗狗說還是對四條狗腿說?

如果你去店里買東西,你會把錢交給店員,還是會把錢包交給店員讓他自己拿?

優點:

迪米特法則使對象之間的耦合降到最小,符合高內聚低耦合的特性,從而使得類具有很好的可讀性和可維護性。

后面介紹的設計模式中,外觀模式(Facade)和中介者模式(Mediator),都是迪米特法則應用的例子。

2. 情景設置

類與類之間的關系越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

解決方案

使用迪米特法則,降低類與類之間的耦合。

3. 代碼示例

示例中有犯人、親人、獄友這些對象,接下來我就通過三個親人去監獄看犯人的設計來說明如何理解迪米特法則(該示例是從網上摘錄的,這里使用Objective-C語言進行重新編寫,并添加了UML圖來進行理解,最后用迪米特法則和依賴倒置原則相結合進行了擴展)。

設計1:親人和獄友有交流(違反迪米特法則,因為對于親人來說,獄友是陌生人)。

UML圖如圖3-1所示:

圖3-1

代碼實現:

(1)Inmates

@interface Inmates : NSObject

- (void)weAreFriend;

@end


@implementation Inmates

- (void)weAreFriend
{
    NSLog(@"獄友說:我們是獄友...");
}

@end

(2)Prisoners

@interface Prisoners : NSObject
{
    Inmates         *_inmates;
}

@property (nonatomic, retain) Inmates *inmates;

- (Inmates *)helpEachOther;

@end


@implementation Prisoners
@synthesize inmates = _inmates;

- (id)init
{
    self = [super init];
    
    if (self != nil) {
        _inmates = [[Inmates alloc] init];
    }
    return self;
}

- (Inmates *)helpEachOther
{
    NSLog(@"家人說:你和獄友之間應該互相幫助...");
    return _inmates;
}

@end

(3)Family

@interface Family : NSObject

- (void)visitPrisoner:(Prisoners *)prisoners;

@end


@implementation Family

- (void)visitPrisoner:(Prisoners *)prisoners
{
    //家人希望犯人與獄友互幫互助
    Inmates *inmates = [prisoners helpEachOther];
    //獄友說,我們是獄友
    [inmates weAreFriend];
}

@end

(4)客戶端調用

Family *family = [[Family alloc] init];
Prisoners *prisoners = [[Prisoners alloc] init];
[family visitPrisoner:prisoners];

從上面可以看到,家人告訴犯人要與獄友好好相處,而獄友卻冒出來說話。這顯然越界了,因為監獄只允許家人探望犯人,而不是隨便誰都可以見。而家人和獄友有了溝通是違背迪米特法則的,所以我們需要將家人和獄友隔離開,對其進行重構。

設計2:家人和獄友沒有直接的交流。

UML圖如圖3-2所示:

圖3-2

代碼實現:
(1)Inmates

@interface Inmates : NSObject

- (void)weAreFriend;

@end


@implementation Inmates

- (void)weAreFriend
{
    NSLog(@"獄友說:我們是獄友...");
}

@end

(2)Prisoners

@interface Prisoners : NSObject
{
    Inmates         *_inmates;
}

@property (nonatomic, retain) Inmates *inmates;

- (void)helpEachOther;

@end


@implementation Prisoners
@synthesize inmates = _inmates;

- (id)init
{
    self = [super init];
    
    if (self != nil) {
        _inmates = [[Inmates alloc] init];
    }
    return self;
}

- (void)helpEachOther
{
     NSLog(@"家人說:你和獄友之間應該互相幫助...");
    [_inmates weAreFriend];
}

@end

(3)Family

@interface Family : NSObject

- (void)visitPrisoner:(Prisoners *)prisoners;

@end


@implementation Family

- (void)visitPrisoner:(Prisoners *)prisoners
{
    [prisoners helpEachOther];
}

@end

(4)客戶端調用

Family *family = [[Family alloc] init];
Prisoners *prisoners = [[Prisoners alloc] init];
[family visitPrisoner:prisoners];

設計3:和依賴倒置原則相結合。

UML圖如圖3-3所示:

圖3-3

代碼實現:
(1)Inmates

@protocol InmatesDelegate <NSObject>

- (void)weAreFriend;

@end

@interface Inmates : NSObject<InmatesDelegate>

@end


@implementation Inmates

- (void)weAreFriend
{
    NSLog(@"獄友說:我們是獄友...");
}

@end

(2)Prisoners

@interface Prisoners : NSObject

- (void)helpEachOther;

@end


@implementation Prisoners

- (void)helpEachOther
{
    NSLog(@"家人說:你和獄友之間應該互相幫助...");
}

@end

(3)Family

@interface Family : NSObject

- (void)visitPrisoner:(Prisoners *)prisoners inmates:(id<InmatesDelegate>)inmates;

@end


@implementation Family

- (void)visitPrisoner:(Prisoners *)prisoners inmates:(id<InmatesDelegate>)inmates
{
    //家人希望犯人與獄友互幫互助
    [prisoners helpEachOther];
    //獄友說,我們是獄友
    [inmates weAreFriend];
}

@end

(4)客戶端調用

Family *family = [[Family alloc] init];
Prisoners *prisoners = [[Prisoners alloc] init];
id<InmatesDelegate> inmates = [[Inmates alloc] init];
[family visitPrisoner:prisoners inmates:inmates];

從上面看到,和依賴倒置原則相結合之后的設計,也是符合迪米特原則的:如果兩個類不必彼此直接通信,那么這兩個類就不應當發生直接的相互作用。如果其中的一個類需要調用另一個類的某一個方法的話,可以通過第三者轉發這個調用,這里的第三者就是接口InmatesDelegate。大話設計模式那本書里面介紹的小菜找IT部修電腦的例子,也是迪米特原則和依賴倒置原則相結合的:在那個例子里面,IT部就是接口(類似這里的InmatesDelegate),小張小李就是具體類(類似這里的Inmates),由于IT部是抽象的,哪怕里面的人都離職或換了新人,小菜的電腦出問題也還是可以找IT部解決,而不需要認識其中的同事,純靠關系幫忙。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容