一個軟體實體應當盡可能少地與其他實體發生互相作用。
作者QQ:1095737364 QQ群:123300273 歡迎加入!
1.定義:
一個軟體實體應當盡可能少地與其他實體發生互相作用。也就是說:一個類對自己依賴的類知道的越少越好。也就是說無論被依賴的類多麼複雜,都應該将邏輯封裝在方法的内部,通過public方法提供給外部。這樣當被依賴的類變化時,才能最小的影響該類。
2.使用場景:
如果類A有兩個職責:d1,d2。當職責d1需要修改時,可能會導緻原本運作正常的職責d2功能産生問題。即如果一個類包含多種職責,就應該把類拆分。分别建立兩個類A、B,讓A負責d1,B負責d2。當需要修改某一職責,那麼将不會對另外一個功能産生影響。
3.使用特點
如果一個系統符合迪米特法則,那麼當其中某一個子產品發生修改時,就會盡量少地影響其他子產品,擴充會相對容易,這是對軟體實體之間通信的限制,迪米特法則要求限制軟體實體之間通信的寬度和深度。迪米特法則可降低系統的耦合度,使類與類之間保持松散的耦合關系。
迪米特法則要求我們在設計系統時,應該盡量減少對象之間的互動,如果兩個對象之間不必彼此直接通信,那麼這兩個對象就不應當發生任何直接的互相作用,如果其中的一個對象需要調用另一個對象的某一個方法的話,可以通過第三者轉發這個調用。簡言之,就是通過引入一個合理的第三者來降低現有對象之間的耦合度。
在将迪米特法則運用到系統設計中時,要注意下面的幾點:在類的劃分上,應當盡量建立松耦合的類,類之間的耦合度越低,就越有利于複用,一個處在松耦合中的類一旦被修改,不會對關聯的類造成太大波及;在類的結構設計上,每一個類都應當盡量降低其成員變量和成員函數的通路權限;在類的設計上,隻要有可能,一個類型應當設計成不變類;在對其他類的引用上,一個對象對其他對象的引用應當降到最低。
4.實作案例
舉一個例子:有一個集團公司,下屬機關有分公司和直屬部門,現在要求列印出所有下屬機關的員工ID。先來看一下違反迪米特法則的設計。
//總公司員工
class Employee{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
}
//分公司員工
class SubEmployee{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
}
class SubCompanyManager{
public List<SubEmployee> getAllEmployee(){
List<SubEmployee> list = new ArrayList<SubEmployee>();
for(int i=0; i<100; i++){
SubEmployee emp = new SubEmployee();
//為分公司人員按順序配置設定一個ID
emp.setId("分公司"+i);
list.add(emp);
}
return list;
}
}
class CompanyManager{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<Employee>();
for(int i=0; i<30; i++){
Employee emp = new Employee();
//為總公司人員按順序配置設定一個ID
emp.setId("總公司"+i);
list.add(emp);
}
return list;
}
public void printAllEmployee(SubCompanyManager sub){
List<SubEmployee> list1 = sub.getAllEmployee();
for(SubEmployee e:list1){
System.out.println(e.getId());
}
List<Employee> list2 = this.getAllEmployee();
for(Employee e:list2){
System.out.println(e.getId());
}
}
}
public class Client{
public static void main(String[] args){
CompanyManager e = new CompanyManager();
e.printAllEmployee(new SubCompanyManager());
}
}
現在這個設計的主要問題出在CompanyManager中,根據迪米特法則,隻與直接的朋友發生通信,而SubEmployee類并不是CompanyManager類的直接朋友(以局部變量出現的耦合不屬于直接朋友),從邏輯上講總公司隻與他的分公司耦合就行了,與分公司的員工并沒有任何聯系,這樣設計顯然是增加了不必要的耦合。按照迪米特法則,應該避免類中出現這樣非直接朋友關系的耦合。修改後的代碼如下:
class SubCompanyManager{
public List<SubEmployee> getAllEmployee(){
List<SubEmployee> list = new ArrayList<SubEmployee>();
for(int i=0; i<100; i++){
SubEmployee emp = new SubEmployee();
//為分公司人員按順序配置設定一個ID
emp.setId("分公司"+i);
list.add(emp);
}
return list;
}
public void printEmployee(){
List<SubEmployee> list = this.getAllEmployee();
for(SubEmployee e:list){
System.out.println(e.getId());
}
}
}
class CompanyManager{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<Employee>();
for(int i=0; i<30; i++){
Employee emp = new Employee();
//為總公司人員按順序配置設定一個ID
emp.setId("總公司"+i);
list.add(emp);
}
return list;
}
public void printAllEmployee(SubCompanyManager sub){
sub.printEmployee();
List<Employee> list2 = this.getAllEmployee();
for(Employee e:list2){
System.out.println(e.getId());
}
}
}
修改後,為分公司增加了列印人員ID的方法,總公司直接調用來列印,進而避免了與分公司的員工發生耦合。
5.注意事項
迪米特法則的初衷是降低類之間的耦合,由于每個類都減少了不必要的依賴,是以的确可以降低耦合關系。但是凡事都有度,雖然可以避免與非直接的類通信,但是要通信,必然會通過一個“中介”來發生聯系,例如本例中,總公司就是通過分公司這個“中介”來與分公司的員工發生聯系的。過分的使用迪米特原則,會産生大量這樣的中介和傳遞類,導緻系統複雜度變大。是以在采用迪米特法則時要反複權衡,既做到結構清晰,又要高内聚低耦合。