I walk very slowly, but I never walk backwards
設計模式原則 - 迪米特法則
寂然
大家好~,我是寂然,本節課呢,我來給大家介紹設計模式原則之迪米特法則,話不多說,我們直接進入正題,老規矩,首先帶大家了解一下迪米特法則的官方定義,并作一個解釋說明,然後我們通過案例代碼來具體分析
前置 - 類的依賴關系
首先,迪米特法則和類之間的關系息息相關,是以在開始之前,我們先對依賴關系做個簡單介紹,便于接下來迪米特法則的入門和了解,後續會有一個章節專門對類的六大關系作一個解讀,大家放心哈
兩個類之間是否具有依賴關系,一句話,凡是在類中用到了對方,那麼他們之間就存在依賴關系(在代碼中沒有對方,編譯都無法通過),例如一個類是另一個類的成員屬性,類中某一個方法的傳回值類型,某一個方法接收的參數類型,方法内部使用到,類中用到了對方等,類的六大關系中,剩餘五種關系都是依賴關系的特例
官方定義
迪米特法則(Law of Demeter, LoD)是1987年秋天由lan holland在美國東北大學一個叫做迪米特的項目設計提出的,它要求一個對象應該對其他對象有最少的了解,是以迪米特法則又叫做最少知識原則(Least Knowledge Principle, LKP)
One object should have a minimum understanding of other objects
(一個對象應該對其他對象有最少的了解 )
Only talk to your immediate friends ( 隻與直接的朋友通信)
基本介紹
首先迪米特法則要說明的一點是,一個類對自己依賴的類知道的越少越好,類與類之間關系越密切,耦合度越大,也就是說,對于被依賴的類,不管多麼複雜,都盡量将邏輯封裝在類的内部,對外除了提供的public方法,不對外洩露任何資訊
迪米特法則還有另一個定義,也就是上面提到的,隻與直接的朋友通信,那什麼是直接的朋友呢?
每個對象都會與其他對象有依賴關系,隻要兩個對象之間有依賴關系,我們就說這兩個對象之間是朋友關系,依賴的方式可以有很多種,我們上面提到過,其中,我們稱出現成員變量,方法參數,方法傳回值中的類為直接的朋友,而出現在局部變量中的類不是直接的朋友,也就是說,陌生的類最好不要以局部變量的形式出現在類的内部
案例示範 - 學院揭秘
下面我們通過一個案例來講解迪米特法則,以及用這段示例代碼來分析下哪些是 “直接的朋友”
有一個學校,下屬有各個學院和總部,現要求列印出學校總部員工 ID 和學院員工的 id ,示例代碼如下:
//學校總部雇員類
class Employee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//學員員工類
class CollegeEmployee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
//管理學院員工的管理類
class CollegeManager{
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> collegeEmployees = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee collegeEmployee = new CollegeEmployee();
collegeEmployee.setId("學院員工id = " + i);
collegeEmployees.add(collegeEmployee);
}
return collegeEmployees;
}
}
//管理總部員工的管理類
class SchoolManager{
public List<Employee> getAllEmployee(){
ArrayList<Employee> employees = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee employee = new Employee();
employee.setId("總部員工id = " + i);
employees.add(employee);
}
return employees;
}
//該方法完成列印學校總部和學院員工id
public void printAllEmployee(CollegeManager collegeManager){
List<CollegeEmployee> collList = collegeManager.getAllEmployee();
System.out.println("---學院員工---");
for (CollegeEmployee collegeEmployee : collList) {
System.out.println(collegeEmployee.getId());
}
List<Employee> list = this.getAllEmployee();
System.out.println("---總部員工---");
for (Employee employee : list) {
System.out.println(employee.getId());
}
}
}
建立測試類,模拟用戶端進行簡單的調用
public class DemeterDemo {
public static void main(String[] args) {
new SchoolManager().printAllEmployee(new CollegeManager());
}
}
案例分析
上面我們對案例進行了簡易的實作,可以看到,我們建一個測試類簡易運作,列印出了學校總部員工 ID 和學院員工的 id ,運作結果很簡單,就不給大家貼圖了, 完成了上面的簡易需求,同時,我們來分析下,上面的示例代碼中,SchoolManager 也就是管理總部員工的管理類,他的直接的朋友有哪些?
首先,Employee 出現在方法的傳回值,是直接的朋友,同理,CollegeManager 類作為 printAllEmployee 方法傳入的參數類型,,也是直接的朋友,那我們再往下看,CollegeEmployee 類,根據上面的規則,該類既不是SchoolManager 的成員變量,也不是出現再方法的參數和傳回值類型中,該類不是直接的朋友關系,而是以局部變量的形式出現,換句話說,根據上面迪米特法則的定義,隻與直接的朋友通信,這樣的寫法違反了迪米特法則
解決方案
既然上面提到,CollegeEmployee 類以局部變量的形式出現在SchoolManager ,不是SchoolManager 的直接朋友,違反了迪米特原則,OK,那針對這個問題,那我們來聊聊,如何對代碼進行改進,使其符合迪米特法則
既然迪米特法則要求我們應該避免類中出現這樣非直接朋友的耦合關系,是以CollegeEmployee 類這裡需要進行調整,上面提到,被依賴的類,不管多麼複雜,都盡量将邏輯封裝在類的内部,那我們就會想到,既然這樣,我們可以把列印學院員工的邏輯,封裝到CollegeManager中,這裡直接調用CollegeManager 封裝好的列印方法即可
這樣就避免了CollegeEmployee 類以局部變量的形式出現,同時 CollegeManager 中 CollegeEmployee 類以方法傳回值的形式出現,是直接的朋友關系,符合迪米特法則的要求
OK,根據上面的想法,我們對案例代碼進行相應的改動,如下圖所示
//管理學院員工的管理類
class CollegeManager{
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> collegeEmployees = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee collegeEmployee = new CollegeEmployee();
collegeEmployee.setId("學院員工id = " + i);
collegeEmployees.add(collegeEmployee);
}
return collegeEmployees;
}
// CollegeManager 完成對學員員工資訊的列印 ------ 改動一
public void printCollegeEmployee(){
List<CollegeEmployee> collList = getAllEmployee();
System.out.println("---學院員工---");
for (CollegeEmployee collegeEmployee : collList) {
System.out.println(collegeEmployee.getId());
}
}
}
//管理總部員工的管理類
class SchoolManager{
public List<Employee> getAllEmployee(){
ArrayList<Employee> employees = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee employee = new Employee();
employee.setId("總部員工id = " + i);
employees.add(employee);
}
return employees;
}
//該方法完成列印學校總部和學院員工id
public void printAllEmployee(CollegeManager collegeManager){
// ------ 改動二
//将輸出學員員工資訊的方法,封裝到 CollegeManager,此處調用即可
collegeManager.printCollegeEmployee();
List<Employee> list = this.getAllEmployee();
System.out.println("---總部員工---");
for (Employee employee : list) {
System.out.println(employee.getId());
}
}
}
注意事項
-
迪米特法則的核心是降低類之間的耦合
(當然,迪米特法則隻是要求降低類與類之間的耦合關系,并不是要求完全沒有依賴關系)
- 從被依賴者的角度來說,盡量将邏輯封裝在類的内部,對外除了提供的public方法,不洩露任何資訊
- 從依賴者的角度來說,隻依賴應該依賴的對象
- 切忌不要為了用而用,正确使用迪米特法則是可以讓程式保證低耦合的,因為避免了與非直接的朋友通信,但是想要通信,就需要用到直接的朋友, 過分的使用迪米特原則,會産生很多這樣沒有必要的直接的朋友,導緻系統複雜度變大,是以,在釆用迪米特法則時要進行權衡,保證系統的結構清晰
下節預告
OK,下一節,我們正式進入設計模式原則之合成複用原則的學習,我會為大家用多個案例分析,來解讀設計模式原則之合成複用原則,以及它的注意事項和細節,最後,希望大家在學習的過程中,能夠感覺到設計模式的有趣之處,高效而愉快的學習,那我們下期見~