1. 何為迪米特法則
狹義的迪米特法則定義:也叫最少知識原則(LKP,Least Knowledge Principle)。如果兩個類不必彼此直接通信,那么這兩個類就不應(yīng)當(dāng)發(fā)生直接的相互作用。如果其中的一個類需要調(diào)用另一個類的某一個方法的話,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用。
廣義的迪米特法則定義:一個模塊設(shè)計(jì)得好壞的一個重要的標(biāo)志就是該模塊在多大的程度上將自己的內(nèi)部數(shù)據(jù)與實(shí)現(xiàn)有關(guān)的細(xì)節(jié)隱藏起來。信息的隱藏非常重要的原因在于,它可以使各個子系統(tǒng)之間解耦,從而允許它們獨(dú)立地被開發(fā)、優(yōu)化、使用閱讀以及修改。
定義解讀:
迪米特法則通常被表述為:Only talk to your immediate friends ( 只和離你最近的朋友進(jìn)行交互)。什么是最近的朋友?我們知道,每個對象都會與其他對象之間有耦合關(guān)系,只要兩個對象之間有耦合關(guān)系,我們就說這兩個對象之間是朋友關(guān)系。耦合的方式很多,如依賴、關(guān)聯(lián)、組合、聚合等。其中,我們稱出現(xiàn)在成員變量、方法參數(shù)、方法返回值中的類為最近的朋友,而出現(xiàn)在局部變量中的類則不是最近的朋友。迪米特法則是很好的解耦合手段之一。在網(wǎng)上看到的比較形象的說明這個法則的示例:
如果你想讓你的狗狗跑的話,你會對狗狗說還是對四條狗腿說?
如果你去店里買東西,你會把錢交給店員,還是會把錢包交給店員讓他自己拿?
優(yōu)點(diǎn):
迪米特法則使對象之間的耦合降到最小,符合高內(nèi)聚低耦合的特性,從而使得類具有很好的可讀性和可維護(hù)性。
后面介紹的設(shè)計(jì)模式中,外觀模式(Facade)和中介者模式(Mediator),都是迪米特法則應(yīng)用的例子。
2. 情景設(shè)置
類與類之間的關(guān)系越密切,耦合度越大,當(dāng)一個類發(fā)生改變時,對另一個類的影響也越大。
解決方案
使用迪米特法則,降低類與類之間的耦合。
3. 代碼示例
示例中有犯人、親人、獄友這些對象,接下來我就通過三個親人去監(jiān)獄看犯人的設(shè)計(jì)來說明如何理解迪米特法則(該示例是從網(wǎng)上摘錄的,這里使用Objective-C語言進(jìn)行重新編寫,并添加了UML圖來進(jìn)行理解,最后用迪米特法則和依賴倒置原則相結(jié)合進(jìn)行了擴(kuò)展)。
設(shè)計(jì)1:親人和獄友有交流(違反迪米特法則,因?yàn)閷τ谟H人來說,獄友是陌生人)。
UML圖如圖3-1所示:
代碼實(shí)現(xiàn):
(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(@"家人說:你和獄友之間應(yīng)該互相幫助...");
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)客戶端調(diào)用
Family *family = [[Family alloc] init];
Prisoners *prisoners = [[Prisoners alloc] init];
[family visitPrisoner:prisoners];
從上面可以看到,家人告訴犯人要與獄友好好相處,而獄友卻冒出來說話。這顯然越界了,因?yàn)楸O(jiān)獄只允許家人探望犯人,而不是隨便誰都可以見。而家人和獄友有了溝通是違背迪米特法則的,所以我們需要將家人和獄友隔離開,對其進(jìn)行重構(gòu)。
設(shè)計(jì)2:家人和獄友沒有直接的交流。
UML圖如圖3-2所示:
代碼實(shí)現(xiàn):
(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(@"家人說:你和獄友之間應(yīng)該互相幫助...");
[_inmates weAreFriend];
}
@end
(3)Family
@interface Family : NSObject
- (void)visitPrisoner:(Prisoners *)prisoners;
@end
@implementation Family
- (void)visitPrisoner:(Prisoners *)prisoners
{
[prisoners helpEachOther];
}
@end
(4)客戶端調(diào)用
Family *family = [[Family alloc] init];
Prisoners *prisoners = [[Prisoners alloc] init];
[family visitPrisoner:prisoners];
設(shè)計(jì)3:和依賴倒置原則相結(jié)合。
UML圖如圖3-3所示:
代碼實(shí)現(xiàn):
(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(@"家人說:你和獄友之間應(yīng)該互相幫助...");
}
@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)客戶端調(diào)用
Family *family = [[Family alloc] init];
Prisoners *prisoners = [[Prisoners alloc] init];
id<InmatesDelegate> inmates = [[Inmates alloc] init];
[family visitPrisoner:prisoners inmates:inmates];
從上面看到,和依賴倒置原則相結(jié)合之后的設(shè)計(jì),也是符合迪米特原則的:如果兩個類不必彼此直接通信,那么這兩個類就不應(yīng)當(dāng)發(fā)生直接的相互作用。如果其中的一個類需要調(diào)用另一個類的某一個方法的話,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用,這里的第三者就是接口InmatesDelegate。大話設(shè)計(jì)模式那本書里面介紹的小菜找IT部修電腦的例子,也是迪米特原則和依賴倒置原則相結(jié)合的:在那個例子里面,IT部就是接口(類似這里的InmatesDelegate),小張小李就是具體類(類似這里的Inmates),由于IT部是抽象的,哪怕里面的人都離職或換了新人,小菜的電腦出問題也還是可以找IT部解決,而不需要認(rèn)識其中的同事,純靠關(guān)系幫忙。