上一篇我們紀錄了依賴反轉原則,到此五大原則介紹完畢…是這樣嗎?太天真了!就像四天王總是五個人一樣,五大原則當然也有第六個!

今天的主角就是五大原則中L位的第一候補:最少知識原則,也被稱作迪米特法則

最少知識原則 (Least Knowledge Principle)

只和直接的朋友溝通,不和陌生人說話

那麼所謂的朋友是什麼呢?就是指這個物件或方法有直接相關的物件啦。例如當我們使用一個方法時,這個方法應該只認識:

  • 該方法所屬的類別
  • 該方法所接收的參數
  • 該方法中建立的類別
  • 該方法所屬的類別所依賴的對象

除此之外對這個方法而言都是陌生人。什麼情況會遇到陌生人呢?有一個蠻常遇到的狀況就符合定義:當我們使用依賴對象的方法,該方法給了我們另一個類別時,我們就正在接觸毫無關係的陌生人。

這個原則的要求就是:不要跟陌生人說話,就算是朋友介紹了他的朋友給你也一樣,不認識就是不認識,更不能拿陌生人的東西。換個方式就是說:不應該使用其他類別的方法所回傳的類別的方法

用文字的可能會有點繞口令,簡單來說就是像 Foo.GetBoo().BooDoSomeThing() 這種情況,我們不該去跟 FooBoo 回來然後使用 Boo 的方法,因為我們只認識 Foo,而不認識 Boo

畢竟,很多時候我們不該直接插手控制:

  • 主人可以叫狗坐下,但主人不應該直接控制狗的腿坐下
  • 當我們按下牆壁的開關時,是希望燈直接打開。而不是彈出兩條電線讓你自己接起來
  • 當我們去餐廳時,會讓服務生替你把要求的餐點交給廚師烹調,而不是我們直接殺進去廚房對著廚師吼「你給我煮啊!」

這種直接叫廚師煮給你看、甚至自己搶過來煮的做法,就是平常直接伸手進去其他模組的控制狂、完全和 封裝 的概念背道而馳。

腿的動作就應該讓狗去自己控制,讓燈泡亮就應該隱藏在開關之後。物件就該只和直接的朋友溝通。

除了只和直接的朋友溝通,也就是只和直接依賴的類別互動,這個互動也是要講究一點的。畢竟朋友之間也還是會有共通的默契和距離,類別之間的互動也應該只做必要的溝通。

這就是我們在封裝提過的「給程式碼隱私的空間」:為了避免物件之間的互動情況過於複雜,我們應該加以控制,把各自的工作封裝在各自的物件內部,使其只有必要的往來。

因此最少知識原則就要求了:一個物件應該對其他物件應該只有最少的了解

到這邊讓我們稍微整理一下:

  • 只和直接的朋友溝通,不和陌生人說話:物件或方法應該只和自己及直接接觸的對象互動
  • 不應該使用其他類別的方法回傳的類別的方法:不該破壞封裝並造成額外且違反邏輯的互動
  • 一個物件對其他物件應該只有最少的了解:類別只開放 (Public) 必要的功能,並且類別之間應該只有必要的互動

也就是說:只依賴應該依賴的對象,只開放應該開放的方法

聰明的朋友應該能從這邊看出最少知識原則的核心理念了,就是解除耦合

我們在 耦合篇 提過,物件彼此有關聯就會產生耦合,而不好的耦合就會散發出臭味。為了方便管理和降低複雜性,減少臭味出現的機率,我們的目標就是追求耦合。

相對於 依賴反轉原則 利用 抽象和介面 的方式在模組之間做出隔離和控制的作法。最少知識原則則是利用 封裝 的概念來解除耦合,畢竟,關聯越少耦合也越少嘛。

所以我們可以說:良好的封裝就是符合最少知識原則的封裝。複雜性隱藏到自己內部,對外只開放必要的功能,並且只使用到直接關聯的對象,確保不會造成意外的耦合,且讓關聯的模組之間更加靠攏。如此一來,就能夠更加提高內聚、降低耦合了。

然而,為了好好地切分朋友和陌生人,也可能會變成需要建立更多的中間類別,或是更多的依賴關係。

例如人原本可以直接把電線接起來讓燈泡亮起來,但為了把電線使燈泡變亮這件事的複雜度封裝起來,我們就必須要有一個開關,再把電線放到開關後面去,變成了人按下開關,開關藉由電線點亮燈炮等等,整體來說會使系統內的類別變多。

因此,在設計的時候也必須要考量到整個方法串的深度,可以用 單一職責 的角度下去衡量。請不要越封裝越細,類別越做越多,反而變成過度設計了。

那麼,今天就記錄到這裡。由於最少知識原則的概念,大多在封裝篇和耦合篇的時候就已經偷渡完了,所以這邊就針對觀念簡單介紹,實務上處理類別間的耦合時,就可以稍微從最少知識原則的角度想一想,一定會有幫助的。那麼,我們下次見~

本系列下一篇:菜雞與物件導向 (Ex1): 小結

參考資料

同系列文章