Java與模式學習筆記 ——設計模式
設計原則是在提高一個系統的可維護性的同時,提高這個系統的可複用性的指導原則。
- “開-閉”原則(OCP, Open-Closed Priciple)
- 里氏代換原則(LSP, Liskov Substitution Principle)
- 依賴倒轉原則(DIP, Dependency Inversion Principle)
- 介面隔離原則(ISP, Interface Segregation Principle)
- 組合/聚合複用原則(CARP, Composition/Aggregation Principle)
- 迪米特法則(LoD, Law of Demeter)
“開-閉”原則
一個軟體實體應當對擴充套件開放,對修改關閉。
所有軟體系統都有一個共同的性質,即對它們的需求都會隨時間的推移而發生變化。在軟體系統面臨新的需求時,系統的設計必須是穩定的。滿足“開-閉”原則的設計有兩個優點:
- 通過擴充套件已有的軟體系統,可以提供新的行為,以滿足對軟體的新需求,使變化中的軟體系統有一定的適應性和靈活性。
- 已有的軟體模組,特別是最重要的抽象層模組不能再修改,這就使變化中的軟體系統有一定的穩定性和延續性。
里氏代換原則
任何基類可以出現的地方,子類一定可以出現。
一個軟體實體如果使用的是一個基類的話,那麼一定適用於其子類,而且它根本不能察覺出基類物件和子類物件的區別,反過來的代換則不成立,即如果一個軟體實體使用的是一個子類的話,那麼它不一定適用於基類。
里氏代換原則是繼承複用的基石。只有當子類可以替換掉父類,軟體單位的功能不會受到影響時,父類才能真正被複用,而子類也才能夠在父類的基礎上增加新的行為。
依賴倒轉原則
要依賴於抽象,不要依賴於實現。
抽象不應當依賴於細節;細節應當依賴於抽象。
要針對介面程式設計,不要針對實現程式設計。
傳統的過程性系統的設計辦法傾向於使高層次的模組依賴於低層次的模組;抽象層依賴於具體層。
抽象層包含的是應用系統的商務邏輯和巨集觀的、對整個系統來說重要的戰略性決定,是必然性的體現;
具體層包含一些次要的與實現有關的演算法和邏輯,以及戰術性決定,帶有相當大的偶然性選擇。
具體層的程式碼會經常有變動,不能避免出現錯誤,如果抽象層依賴於具體層,會使許多具體層的細節的演算法變動立即影響到抽象層的巨集觀的商務邏輯,導致微觀決定巨集觀,戰術決定戰略,偶然決定必然。
抽象 x = new 具體();
假設“蛋”就是java介面或者抽象類,“雞”是一個具體類,x是一個變數
應當:蛋 x = new 雞();
而不應當: 雞 x = new 雞();
應當: List employees = new Vector();
不應當: Vector employees = new Vector();
若將Vector型別轉換成ArrayList時,需要改動得很少: List employees = new ArrayList();
介面隔離原則
應當為客戶端提供儘可能小的單獨的介面,而不要提供大的總介面。
使用多個專門的介面比使用單一的總介面要好。
一個類對另外一個類的依賴性應當是建立在最小的介面上的。
組合/聚合複用原則
儘量使用組合/聚合,而不是繼承關係達到複用的目的。
聚合(Aggregation):表示“擁有”關係或者整體與部分的關係;
合成(Composition):表示一種強得多的“擁有”關係,部分和整體的生命週期是一樣的。
迪米特法則
一個軟體實體應當與儘可能少的其他實體發生相互作用。
如果兩個類不必彼此直接通訊,那麼這兩個類就不應當發生直接的相互作用。如果其中的一個類需要呼叫另一個類的某一個方法的話,可以通過第三者轉發這個呼叫。
Someone與Friend是朋友,而Friend與Stranger是朋友。
public class Someone {
public void operation1(Friend friend) {
Stranger stranger = friend.provide();
stranger.operation3();
}
}
public class Friend {
private Stranger stranger = new Stranger();
public void operation2() {}
public Stranger provide() {
return stranger;
}
}
顯然,Someone的方法operation1()不滿足迪米特法則。因為該方法引用了Stranger物件,而Stranger物件不是Someone的朋友。
改造後:
切斷Someone與Stranger之間的聯絡
public class Someone {
public void operation1(Friend friend) {
friend.forward();
}
}
public class Friend {
private Stranger stranger = new Stranger();
public void operation2() {
System.out.println("In Friend.operation2()");
}
public void forward() {
stranger.operation3();
}
}
缺點:
會在系統裡造出大量的小方法,散落在系統的各個角落。這些方法僅僅是傳遞間接的呼叫,因此與系統的商務邏輯無關。當設計師試圖從一張類圖看出總體的架構時,這些小的方法會造成迷惑和困擾。
會使一個系統的區域性設計簡化,因為每一個區域性都不會和遠距離的物件直接的關聯,會造成系統的不同模組之間的通訊效率降低,也會使系統的不同模組之間不容易協調。
迪米特法則運用到系統設計中時,要注意以下幾點:
- 在類的劃分上,應當建立有弱耦合的類。類之間的耦合越弱,就越有利於複用。一個處在弱耦合中的類一旦被修改,不會對有關係的類造成波及。
- 在類的結構設計上,每一個類都應當儘量降低成員的訪問許可權。換言之,一個類包裝好各自的private狀態。這樣一來,想要了解其中的一個類的意義時,不需要了解很多別的類的細節。一個類不應當public自己的屬性,而應當提供取值和賦值方法讓外界間接訪問自己的屬性。
- 一個類應當設計成不變類。
- 在對其他類的引用上,一個物件對其物件的引用應當降到最低。