增刪改查也有設計模式——依賴倒置原則另解
依賴倒置原則介紹
依賴倒置原則包括兩個部分
- .高層次的模塊不應該依賴於低層次的模塊,他們都應該依賴於抽象。
- 抽象不應該依賴於具體實現,具體實現應該依賴於抽象。
例子
現在有如下場景和需求:
老板要求設計任務模塊,包括發布任務和撤回任務等。
假設這個需求只給了幾個小時去做,那肯定是來不及設計了,寫到哪算哪。
定義撤回接口的控制層如下
@RequestMapping(‘cancel‘) @ResponseBody public String cancelTask(Task task){ // TODO 調用service的撤回接口 return "{‘status‘:‘success‘}"; }
重點來了
這個時候已經寫到這個地方了,服務層的cancel接口還沒寫好,因為在SpringMVC數據自動綁定的時候有了一個task對象,自然而然會聯想到定義如下接口
public void cancel(Task task) throws ServiceException{};
而不是
public void cancel(String taskId) throws ServiceException{}
因為我手頭有一個現成的task對象,我首先想到的傳遞參數就是task。兩種差異也很簡單,第二個需要在代碼裏多一個
Task task = this.findOneById(taskId); //因為是在service層的代碼,一般都有findOneById這個接口
好了,因為我已經有task,所以這個時候我會很自然的給我兩種暗示:
- 如果我接受的參數是taskId我需要在和數據庫做IO,如果調用方已經IO過一次了那我這個動作不是多余的嗎
- 調用方應該可以保證傳給我的task是正確的數據
這裏已經出現了錯誤的依賴的問題了
1 在定義接口時我們假設傳進來的參數是數據庫中最新的,但是這個根本無法保證。撤銷接口的職責是什麽?是將任務的狀態從處理中變為待處理
2 高層和底層都依賴了實現,因為這裏是基於我已經有了task對象,不想再IO消耗性能這段已經存在的代碼邏輯去定義接口的
正確的應該是
當定義撤回接口時應該拋開已有的代碼,去想這個接口的職責:變更狀態,做其他操作
1 變更狀態只需要id即可,其他操作也不依賴於高層的傳遞參數
這個就是
- .高層次的模塊不應該依賴於低層次的模塊,他們都應該依賴於抽象。
- 抽象不應該依賴於具體實現,具體實現應該依賴於抽象。
同樣也是面向接口編程
上面是自己開發過程中發現的另解,通解如下
依賴倒置原則實際講的是高層調用低層時不應該通過實現類去調用,而是通過接口去調用,這樣一旦低層出現了較大的變動,上層不會有太大波動。
假設如下場景
學生讀書
public class Student{
public void read(Honglou honglou){
// 遍歷每一行
for(Line line: honglou.getLines){
// 小明一行一行看
this.see(line);
}
}
}
public class Honglou{
public List<Line> getLines(){};
}
此時小明依賴於具體類Honglou,一旦小明想讀其他書(使用其他實現)就無法實現了。
如果此時定義一個IBook類作為基類:
public class Student{
public void read(IBook book){
// 遍歷每一行
for(Line line: book.getLines){
// 小明一行一行看
this.see(line);
}
}
}
public interface IBook{
public List<Line> getLines();
}
這樣一旦小明要看其他的書,在參數傳遞時修改具體派生對象就可以了。
這個原則屬於設計模式中比較常用的原則,很多設計模式都是定義一些派生類去實現接口。如抽象工廠類,定義一組接口,由具體工廠類去實現,當需要切換其他模式的時候,切換實現類即可。
策略模式:定義一個算法接口,要求他們可以隨時切換,不同的算法派生類都實現同一個接口。算法調用類依賴於接口而不是具體策略類。
增刪改查也有設計模式——依賴倒置原則另解