軟體設計模式之SOLID原則
By:授客 QQ:1033553122
#單一職責原則(SRP)
定義:任何一個軟體模塊都只對某一類行為者負責
說明:這里“軟體模塊”,在大部分情況下,可以簡單定義為一個源代碼檔案、一個類、一組緊密相關的函式和資料結構、
#開閉原則(OCP)
定義:軟體物體應當對擴展開放,對修改關閉
說明:這里的“軟體物體”包含模塊,類,介面,方法等
開閉原意在告訴我們,當應用的需求改變時,在不修改軟體物體原有的源代碼或者二進制代碼的前提下,可以通過新增代碼來滿足新的需求,也就是說一個設計良好的計算機系統應該在不需要修改的前提下就可以輕易被擴展,這是架構的根本目的,如果對原始需求的小小延伸就需要對原有的軟體系統進行大幅修改,那么這個系統的架構設計顯然是失敗的,
在Java、C++這類語言中,可以通過“抽象約束、封裝變化”來實作開閉原則,即通過介面或者抽象類為軟體物體定義一個相對穩定的抽象層,而將相同的可變因素封裝在相同的具體實作類中, 因為抽象靈活性好,適應性廣,只要抽象的合理,可以基本保持軟體架構的穩定,而軟體中易變的細節可以從抽象派生來的實作類來進行擴展,當軟體需要發生變化時,只需要根據需求重新派生一個實作類來擴展就可以了,
而在python中一切都是物件,可以指向任何型別,所以,不用定義介面變可實作類似介面,
#里氏替換原則(LSP)
第一種定義:如果對每一個型別為S的物件o1,都有型別為T的物件o2,使得以T定義的所有程式P在所有的物件o1都代換成o2 時,程式P的行為沒有發生變化,那么型別 S 是型別 T 的子型別,
第二種定義:所有參考基類的地方必須能透明地使用其子類的物件,
第一種定義是最正宗的定義,而第二種定義則是最清晰明確的,通俗點講,只要父類能出現的地方子類就可以出現,而且替換為子類也不會 產生任何錯誤或例外,使用者可能根本就不需要知道是父類還是子類,但是,反過來就不行了,有子類出現的地方,父類未必就能適應
里氏替換原則是繼承復用的基石,只有當衍生類可以替換基類,軟體單位的功能不受到影響時,即基類隨便怎么改動子類都不受此影響,那么基類才能真正被復用
因為繼承帶來的侵入性,增加了耦合性,也降低了代碼靈活性,父類修改代碼,子類也會受到影響,要讓程式遵守里氏替換原則,實作繼承時必須遵守以下幾點:
1)子類必須實作父類的抽象方法,但不得重寫(覆寫)父類的非抽象(已實作)方法,
2)當子類覆寫或實作父類的方法時,方法的的形參要比父類方法的輸入引數更寬松,
3)當子類的方法實作父類的抽象方法時,方法的回傳值要比父類更嚴格,
4)遵守以上幾點的情況下,無法滿足需求時,可以考慮在子類中增加自己特有的方法,
#介面隔離原則(ISP)
定義:
1、客戶端不應該依賴它不需用的介面
2、類間的依賴關系應該建立在最小的介面上,
簡單理解就是,不要在一個介面里面放很多的方法,這樣會顯得這個類很臃腫,java介面類為例,繼承介面的非抽象子類,都要實作介面類的擁有的所有方法,所以,當這些子類僅需要要介面類中的部分方法時還是需要去實作對其沒有意義的介面方法,所以,介面應該盡量細化,一個介面對應一個功能模塊,同時介面里面的方法應該盡可能的少,使介面更加靈活輕便,但是需要注意的是:拆分要適度度,對介面進行細化可以提高程式設計靈活性是不掙的事實,但是如果過小,則會造成介面數量過多,使設計復雜化,所以一定要適度,
介面隔離原則和單一職責原則雖然很類似,但是兩個原則還是存在著明顯的區別,單一職責原則是在業務邏輯上的劃分,注重的是職責,介面隔離原則是基于介面設計考慮,
#依賴反轉原則(DIP)
依賴反轉原則被稱作依賴倒置原則,
定義:
1)高層策略性的代碼不應該依賴實作底層細節的代碼
2)抽象不應該依賴于細節,細節應該依賴于抽象
說明:
1、什么是“高層”,什么是“細節”?
對一個系統來說,業務邏輯是高層,其他是細節,業務邏輯是僅僅包括用例、業務物體部分,不包括任何框架、存盤(資料庫)、其他系統等部分,是純粹的,其他細節,包括框架、資料庫、訊息佇列,都是細節,業務邏輯應該不依賴任何細節,細節的實作可以任意替換而不影響業務邏輯,
依賴倒轉原則是基于這樣的設計理念:相對于細節的多變性,抽象的東西要穩定的多,以抽象為基礎搭建的架構比以細節為基礎的架構要穩定得多,其中心思想是面向介面編程
該原則告訴我們,如果想要設計一個靈活的系統,在源代碼層次的依賴關系中就應該多參考穩定的抽象型別,而非具體實作,特別注意不要在具體實作類上創建衍生類,不要覆寫包含具體實作的函式,Java中,抽象多指的是介面或抽象類,用介面或抽象類的目的是制定好規范,而不涉及任何具體的操作,把展現細節的任務交給他們的實作類去完成,
顯而易見,把這條設計原則當成金科玉律來加以嚴格執行是不現實的,因為在實際構造系統的程序中,不可避免的依賴一些具體實作,比如java的String類就是這樣一個具體實作,我們將其強迫的轉化為抽象類是不現實的,類似String類這種本身是非常穩定的類、模塊,可以不用考慮,需要多關注的是經常會變動的具體實作模塊,
在Python中,可以不通過抽象類的方式很輕松的實作依賴反轉
例子:音樂玩具播放器模擬程式,要求可以播放各種動物的聲音,
最開始,這個玩具的要求比較簡單,一開始只要求播放一種動物聲音,鳥叫聲
Bird.java
publicclass Bird{
publicvoid call(){
System.out.println("bird call");
}
}
ToyPlayer.java
publicclass ToyPlayer{
publicvoid play(Bird bird){ #這里的入參,參考的是具體的實作類
bird.call();
}
}
Entry.java
publicclass Entry {
publicstaticvoid main(String[] args){
ToyPlayer player = new ToyPlayer();
Bird bird = new Bird();
player.play(bird);
}
}
如上,以上代碼是不符合依賴反轉原則的,播放器類,依賴具體的動物類(實作類),當需求變化時可能無法滿足需求,比如,需要給玩具增加其它動物聲,比如狗叫,這個時候就需要更改程式了,
改進版
Animal.java
interface Animal{
publicvoid call();
}
Bird.java
publicclass Bird implements Animal{
publicvoid call(){
System.out.println("bird call");
}
}
Dog.java
publicclass Dog implements Animal{
publicvoid call(){
System.out.println("dog call");
}
}
ToyPlayer.java
publicclass ToyPlayer{
publicvoid play(Animal animal){ #注意,這里替換了引數型別--替換具體型別別 Bird 為抽象型別別 Animal
animal.call();
}
}
Entry.java
publicclass Entry {
publicstaticvoid main(String[] args){
ToyPlayer player = new ToyPlayer();
Bird bird = new Bird();
player.play(bird);
Dog dog = new Dog();
player.play(dog);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/255544.html
標籤:其他
