個人博客原文: "依賴倒置原則" 設計模式六大原則之三:依賴倒置原則。 簡介 姓名 :依賴倒置原則 英文名 :Dependence Inversion Principle 價值觀 :大男子主義的典型代表,什麼都得通過老大或者老爸同意 伴侶 :一定是個溫柔體貼的女子 個人介紹 : 1. High le ...
個人博客原文:
依賴倒置原則
設計模式六大原則之三:依賴倒置原則。
簡介
姓名 :依賴倒置原則
英文名 :Dependence Inversion Principle
價值觀 :大男子主義的典型代表,什麼都得通過老大或者老爸同意
伴侶 :一定是個溫柔體貼的女子
個人介紹 :
- High level modules should not depend upon low level modules.Both should depend upon abstractions. 高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象(模塊間的依賴通過抽象發生,實現類之間不發生直接的依賴關係,其依賴關係是通過介面或抽象類產生的)
- Abstractions should not depend upon details. 抽象不應該依賴細節(介面或抽象類不依賴於實現類)
- Details should depend upon abstractions. 細節應該依賴抽象(實現類依賴介面或抽象類)
給大家講個故事,我胡亂想的,如有雷同,肯定是英雄所見略同。那必須交個朋友。
一個小村裡,有兩家飯館,雖然掛著不同的牌子,挨在一起,但是老闆確是表兄弟。這兩兄弟摳得很,為了節省成本,密謀了一個想法:在兩家飯館誰家忙的時候,可以讓不忙的那家的員工過去支援一下。這樣子,本來每家飯館都需要 2 個洗碗工,總共需要 4 個,他們就只招了 3 個,省了 1 個洗碗工的成本,當然不止洗碗工,還有服務員等等。兩兄弟約定了規則:
- A 飯館需要支援的時候,B 飯館老闆,讓 B 飯館老闆選哪個員工去支援,不能直接讓 A 飯館的員工直接找 B 飯館的員工去幫忙,但可以讓 A 飯館員工找 B飯館老闆告知需要支援。
- 雖然老闆權利大,但是也不能說 A 飯館老闆直接叫 B 飯館的員工去幫忙。
- 員工沒有真實的老闆,今天為 A 飯館工作就是 A 飯館的員工,沒有跟定哪個老闆。
大概通過這個小故事,描述了依賴倒置原則的基本內容。
代碼複原
下麵通過代碼來模擬這個故事。
錯誤的示範
這個錯誤的示範將就看哈,可能有些問題沒描述清楚。
老闆和員工抽象
abstract class Boss {
abstract void support();
abstract void askHelp(Boss boss);
}
abstract class Staff {
private String name;
abstract void service();
abstract void askHelp(Boss boss);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
老闆具體類
class BossA extends Boss {
private StaffA staffA;
public BossA(StaffA staffA) {
this.staffA = staffA;
}
@Override
void support() {
staffA.service();
}
@Override
void askHelp(Boss boss) {
boss.support();
}
}
class BossB extends Boss {
private StaffB staffB;
public BossB(StaffB staffB) {
this.staffB = staffB;
}
@Override
void support() {
staffB.service();
}
@Override
void askHelp(Boss boss) {
boss.support();
}
}
員工具體類
class StaffA extends Staff {
public StaffA(String name) {
this.setName(name);
}
@Override
void service() {
System.out.println(this.getName() + "提供服務");
}
@Override
void askHelp(Boss boss) {
boss.support();
}
}
class StaffB extends Staff {
public StaffB(String name) {
this.setName(name);
}
@Override
void service() {
System.out.println(this.getName() + "提供服務");
}
@Override
void askHelp(Boss boss) {
boss.support();
}
}
測試代碼
/** 初始化老闆和員工 */
StaffA staffA = new StaffA("A 員工");
StaffB staffB = new StaffB(" B 員工");
Boss bossA = new BossA(staffA);
Boss bossB = new BossB(staffB);
/** A 老闆向 B 老闆求支援 */
bossA.askHelp(bossB); // 列印出:B 員工提供服務
/** B 員工向 A 老闆求支援 */
staffB.askHelp(bossA); // 列印出:A 員工提供服務
好像看起來實現了要求了,但是其實這段代碼沒有按照上面的 3 點規則編寫,破壞了第 3 點規則,老闆們的員工沒有用員工的抽象類,破壞了細節依賴抽象這一點。設想一下,假如現在 A 老闆把 A 員工辭退了,重新招了個 C 員工,那麼怎麼實現呢?是不是需要再新增一個 StaffC 類,然後再修改 BossA 類代碼,把 StaffA 換成 StaffC。這樣超級麻煩,在平時寫項目中要時刻考慮這一點:在具體實現類使用其他類,是不是可以用其抽象類?
代碼:
正確的示範
看了上面那個憋屈的代碼,再來看下麵簡潔的代碼,才會發現依賴倒置原則是多麼強大。
老闆和員工抽象類
abstract class Boss2 {
private Staff2 staff;
public Boss2(Staff2 staff) {
this.staff = staff;
}
abstract void support();
abstract void askHelp(Boss2 boss);
public void setStaff(Staff2 staff) {
this.staff = staff;
}
public Staff2 getStaff() {
return staff;
}
}
abstract class Staff2 {
private String name;
abstract void service();
abstract void askHelp(Boss2 boss);
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
老闆類
class BossImpl extends Boss2 {
public BossImpl(Staff2 staff) {
super(staff);
}
@Override
void support() {
this.getStaff().service();
}
@Override
void askHelp(Boss2 boss) {
boss.support();
}
}
員工類
class StaffImpl extends Staff2{
public StaffImpl(String name) {
this.setName(name);
}
@Override
void service() {
System.out.println(this.getName() + "提供服務");
}
@Override
void askHelp(Boss2 boss) {
boss.support();
}
}
測試類
/** 正確示範 */
Staff2 staffA2 = new StaffImpl("A 員工");
Staff2 staffB2 = new StaffImpl("B 員工");
Boss2 bossA2 = new BossImpl(staffA2);
Boss2 bossB2 = new BossImpl(staffB2);
/** A 老闆向 B 老闆求支援 */
bossA2.askHelp(bossB2); // 列印出:B 員工提供服務
/** B 員工向 A 老闆求支援 */
staffB2.askHelp(bossA2); // 列印出:A 員工提供服務
/** A 老闆辭退了 A 員工,換成了 C 員工 */
Staff2 staffC2 = new StaffImpl("C 員工");
bossA2.setStaff(staffC2);
/** B 員工向 A 老闆求支援 */
staffB2.askHelp(bossA2); // 列印出:C 員工提供服務
這代碼相比上面錯誤的示範,簡潔了很多,實現的功能卻更靈活,這就是依賴倒置原則強大的地方,它可以將類的耦合性降低,提供靈活的處理。
代碼:
最佳實踐
- 變數的錶面類型儘量是介面或者是抽象類
- 任何類都不應該從具體類派生
- 儘量不要覆寫基類的方法
- 結合里氏替換原則使用
(來自《設計模式之禪》)
總結
總的來說,要實現依賴倒置原則,要有『面向介面編程』這個思維,掌握好這個思維後,就可以很好的運用依賴倒置原則。
參考資料:《大話設計模式》、《Java設計模式》、《設計模式之禪》、《研磨設計模式》、《Head First 設計模式》
歡迎大家關註公眾號 LieBrother,一起學習、進步!