定義
迪米特法則(Law of Demeter, LoD)是1987年秋天由lan holland在美國東北大學(xué)一個叫做迪米特的項目設(shè)計提出的,它要求一個對象應(yīng)該對其他對象有最少的了解洲赵,所以迪米特法則又叫做最少知識原則(Least Knowledge Principle, LKP)睛驳。
意義
迪米特法則的意義在于降低類之間的耦合辜梳。由于每個對象盡量減少對其他對象的了解泊柬,因此蜜唾,很容易使得系統(tǒng)的功能模塊功能獨立杂曲,相互之間不存在(或很少有)依賴關(guān)系。
值得一提的是袁余,這一法則卻不僅僅局限于計算機(jī)領(lǐng)域擎勘,在其他領(lǐng)域也同樣適用。比如颖榜,美國人就在航天系統(tǒng)的設(shè)計中采用這一法則棚饵。
實踐
那么在實踐中如何做到一個對象應(yīng)該對其他對象有最少的了解呢?如果我們把一個對象看作是一個人掩完,那么要實現(xiàn)“一個人應(yīng)該對其他人有最少的了解”噪漾,做到兩點就足夠了:1.只和直接的朋友交流;2.減少對朋友的了解且蓬。下面就詳細(xì)說說如何做到這兩點欣硼。
只和直接的朋友交流
迪米特法則還有一個英文解釋是:talk only to your immediate friends(只和直接的朋友交流)。什么是朋友呢恶阴?每個對象都必然會與其他的對象有耦合關(guān)系诈胜,兩個對象之間的耦合就會成為朋友關(guān)系。那么什么又是直接的朋友呢冯事?出現(xiàn)在成員變量焦匈、方法的輸入輸出參數(shù)中的類就是直接的朋友。迪米特法則要求只和直接的朋友通信昵仅。
注意:
只出現(xiàn)在方法體內(nèi)部的類就不是直接的朋友缓熟,如果一個類和不是直接的朋友進(jìn)行交流,就屬于違反迪米特法則。
我們舉一個例子說明什么是朋友够滑,什么是直接的朋友垦写。很簡單的例子:老師讓班長清點全班同學(xué)的人數(shù)。這個例子中總共有三個類:老師Teacher
版述、班長GroupLeader
和學(xué)生Student
梯澜。
老師類:
@interface Teacher : NSObject
//命令班長清點人數(shù)
- (void)command:(GroupLeader *)groupLeader;
@end
@implementation Teacher
- (void)command:(GroupLeader *)groupLeader{
//創(chuàng)建全班學(xué)生的數(shù)組
NSMutableArray<Student *> *students = [[NSMutableArray alloc] init];
for (int i = 0; i < 20; i++) {
Student *student = [[Student alloc] init];
[students addObject:student];
}
//讓班長清點人數(shù)
[groupLeader count:students];
}
@end
班長類:
@interface GroupLeader : NSObject
//清點人數(shù)
- (void)count:(NSArray<Student *> *)students;
@end
@implementation GroupLeader
- (void)count:(NSArray<Student *> *)students{
//直接打印出學(xué)生的人數(shù)
NSLog(@"上課人數(shù)是:%d",(int)students.count);
}
@end
學(xué)生類:
@interface Student : NSObject
@end
@implementation Student
@end
使用場景也是非常的簡單:
int main(int argc, const char * argv[]) {
//創(chuàng)建班長對象
GroupLeader *groupLeader = [[GroupLeader alloc] init];
//創(chuàng)建老師對象
Teacher *teacher = [[Teacher alloc] init];
//老師命令班長清點人數(shù)
[teacher command:groupLeader];
return 0;
}
在這個例子中,我們的Teacher
有幾個朋友渴析?兩個晚伙,一個是GroupLeader
,因為它是Teacher
的command:
方法的輸入?yún)?shù)俭茧;另一個是Student
咆疗,因為在Teacher
的command:
方法體中使用了Student
。
那么我們的Teacher
有幾個是直接的朋友母债?按照直接的朋友的定義“出現(xiàn)在成員變量午磁、方法的輸入輸出參數(shù)中的類就是直接的朋友”,只有GroupLeader
是Teacher
的直接的朋友毡们。
Teacher
在command
方法中創(chuàng)建了Student
的數(shù)組迅皇,和非直接的朋友Student
發(fā)生了交流,所以衙熔,上述例子違反了迪米特法則登颓。方法是類的一個行為,類竟然不知道自己的行為與其他的類產(chǎn)生了依賴關(guān)系红氯,這是不允許的框咙,嚴(yán)重違反了迪米特法則!
為了使上述例子符合迪米特法則痢甘,我們可以做如下修改:
修改后的GroupLeader:
@interface GroupLeader : NSObject
//構(gòu)造方法喇嘱,傳入全班學(xué)生數(shù)組
- (instancetype)initWithStudents:(NSArray<Student *> *)students;
//清點人數(shù)方法
- (void)count;
@end
@implementation GroupLeader
{
NSArray<Student *> *_students;
}
- (instancetype)initWithStudents:(NSArray<Student *> *)students{
self = [super init];
if (self) {
_students = students;
}
return self;
}
- (void)count{
NSLog(@"上課人數(shù)是:%d",(int)_students.count);
}
@end
修改后的老師類:
@interface Teacher : NSObject
//命令班長執(zhí)行清點人數(shù)的任務(wù)
- (void)command:(GroupLeader *)groupLeader;
@end
@implementation Teacher
- (void)command:(GroupLeader *)groupLeader{
[groupLeader count];
}
@end
修改后的使用場景:
int main(int argc, const char * argv[]) {
//創(chuàng)建學(xué)生數(shù)組
NSMutableArray *students = [[NSMutableArray alloc] init];
for (int i = 0; i < 20; i++) {
Student *student = [[Student alloc] init];
[students addObject:student];
}
//創(chuàng)建班長
GroupLeader *groupLeader = [[GroupLeader alloc] initWithStudents:students];
//創(chuàng)建老師
Teacher *teacher = [[Teacher alloc] init];
//老師命令班長執(zhí)行清點人數(shù)命令
[teacher command:groupLeader];
return 0;
}
這樣修改后,每個類都只和直接的朋友交流塞栅,有效減少了類之間的耦合者铜。
減少對朋友的了解
如何減少對朋友的了解?如果你的朋友是個話癆加大喇叭放椰,那就算你不主動去問他作烟,他也會在你面前叨叨叨,把他所有的經(jīng)歷都講給你聽庄敛。所以,要減少對朋友的了解科汗,請換一個內(nèi)斂一點的朋友吧~換作在一個類中藻烤,就是盡量減少一個類對外暴露的方法。
舉一個簡單的例子說明一個類暴露方法過多的情況。這個例子描述的是一個人用咖啡機(jī)煮咖啡的過程怖亭,例子中只有兩個類涎显,一個是人,一個是咖啡機(jī)兴猩。
首先是咖啡機(jī)類CoffeeMachine
期吓,咖啡機(jī)制作咖啡只需要三個方法:1.加咖啡豆;2.加水倾芝;3.制作咖啡:
@interface CoffeeMachine : NSObject
//加咖啡豆
- (void)addCoffeeBean;
//加水
- (void)addWater;
//制作咖啡
- (void)makeCoffee;
@end
@implementation CoffeeMachine
- (void)addCoffeeBean{
//自行腦補...
}
- (void)addWater{
//自行腦補...
}
- (void)makeCoffee{
//自行腦補...
}
@end
然后就是人類Man
讨勤,該類只有一個方法makeCoffee
,在該方法中使用咖啡機(jī)制作咖啡:
@interface Man : NSObject
//構(gòu)造方法
- (instancetype)initWithCoffeeMachine:(CoffeeMachine *)coffeeMachine;
//制作咖啡方法
- (void)makeCoffee;
@end
@implementation Man
{
CoffeeMachine *_coffeeMachine;
}
- (instancetype)initWithCoffeeMachine:(CoffeeMachine *)coffeeMachine{
self = [super init];
if (self) {
_coffeeMachine = coffeeMachine;
}
return self;
}
- (void)makeCoffee{
//使用咖啡機(jī)制作咖啡
[_coffeeMachine addCoffeeBean];
[_coffeeMachine addWater];
[_coffeeMachine makeCoffee];
}
@end
使用場景也非常的簡單:
int main(int argc, const char * argv[]) {
//創(chuàng)建咖啡對象
CoffeeMachine *coffeeMachine = [[CoffeeMachine alloc] init];
//創(chuàng)建人
Man *man = [[Man alloc] initWithCoffeeMachine:coffeeMachine];
//人制作咖啡
[man makeCoffee];
return 0;
}
在這個例子中晨另,CoffeeMachine
是Man
的直接好友潭千,但問題是Man
對CoffeeMachine
了解的太多了,其實人根本不關(guān)心咖啡機(jī)具體制作咖啡的過程借尿。所以我們可以作如下優(yōu)化:
優(yōu)化后的咖啡機(jī)類刨晴,只暴露一個work
方法,把制作咖啡的三個具體的方法addCoffeeBean
路翻、addWater
狈癞、makeCoffee
設(shè)為私有:
@interface CoffeeMachine : NSObject
//只暴露一個方法
- (void)work;
@end
@implementation CoffeeMachine
- (void)work{
//在work方法中執(zhí)行完整的制作咖啡的過程
[self addCoffeeBean];
[self addWater];
[self makeCoffee];
}
- (void)addCoffeeBean{
//自行腦補...
}
- (void)addWater{
//自行腦補...
}
- (void)makeCoffee{
//自行腦補...
}
@end
現(xiàn)在Man
對CoffeeMachine
的了解只有一個work
方法了,所以Man
類應(yīng)該修改為:
@interface Man : NSObject
//構(gòu)造方法
- (instancetype)initWithCoffeeMachine:(CoffeeMachine *)coffeeMachine;
//制作咖啡方法
- (void)makeCoffee;
@end
@implementation Man
{
CoffeeMachine *_coffeeMachine;
}
- (instancetype)initWithCoffeeMachine:(CoffeeMachine *)coffeeMachine{
self = [super init];
if (self) {
_coffeeMachine = coffeeMachine;
}
return self;
}
- (void)makeCoffee{
//使用咖啡機(jī)制作咖啡
[_coffeeMachine work];
}
@end
這樣修改后茂契,通過減少CoffeeMachine
對外暴露的方法蝶桶,減少Man
對CoffeeMachine
的了解,從而降低了它們之間的耦合账嚎。
在實踐中莫瞬,只要做到只和直接的朋友交流和減少對朋友的了解,就能滿足迪米特法則郭蕉。因此我們不難想象疼邀,迪米特法則的目的,是把我們的類變成一個個“肥宅”召锈∨哉瘢“肥”在于一個類對外暴露的方法可能很少,但是它內(nèi)部的實現(xiàn)可能非常復(fù)雜(這個解釋有點牽強(qiáng)~)涨岁」胀啵“宅”在于它只和直接的朋友交流。在現(xiàn)實生活中“肥宅”是個貶義詞梢薪,在日本“肥宅”已經(jīng)成為社會問題蹬铺。但是在程序中,一個“肥宅”的類卻是優(yōu)秀類的典范秉撇。
注意
迪米特法則的核心觀念就是類間解耦甜攀,弱耦合秋泄。只有弱耦合了之后,類的復(fù)用才可以提高规阀,類變更的風(fēng)險才可以減低恒序。但解耦是有限度的,除非是計算機(jī)的最小單元--二進(jìn)制的0和1谁撼,否則都是存在耦合的歧胁。所以在實際項目中,需要適度地參考這個原則厉碟,避免過猶不及喊巍。