1. 程式人生 > >OO設計模式五大原則

OO設計模式五大原則

現將近期整理的文件提供給大家,這裡對LSP做重點的介紹,望對大家有幫助,在學習和使用OO設計的時候,我們應該明白:OO的出現使得軟體工程師們能夠用更接近真實世界的方法描述軟體系統。然而,軟體畢竟是建立在抽象層次上的東西,再怎麼接近真實,也不能替代真實或被真實替代。 
OO設計的五大原則之間並不是相互孤立的。彼此間存在著一定關聯,一個可以是另一個原則的加強或是基礎。違反其中的某一個,可能同時違反了其餘的原則。因此應該把這些原則融會貫通,牢記在心! 
OO的五大原則是指SRP、OCP、LSP、DIP、ISP。 
1. SRP(Single Responsibility Principle 單一職責原則) 
單一職責很容易理解,也很容易實現。所謂單一職責,就是一個設計元素只做一件事。什麼是“只做一件事”?簡單說就是少管閒事。現實中就是如此,如果要你專心做一件事情,任何人都有信心可以做得很出色。 
OCP作為OO的高層原則,主張使用“抽象(Abstraction)”和“多型(Polymorphism)”將設計中的靜態結構改為動態結構,維持設計的封閉性。 
2. OCP :開閉原則,很簡單,一句話:“Closed for Modification; Open for Extension”——“對變更關閉;對擴充套件開放”。開閉原則其實沒什麼好講的,我將其歸結為一個高層次的設計總則。OCP的動機很簡單:軟體是變化的。不論是優質的設計還是低劣的設計都無法迴避這一問題。OCP說明了軟體設計應該儘可能地使架構穩定而又容易滿足不同的需求。 為什麼要OCP?答案也很簡單——重用。 
3.LSP——里氏替換原則 
OCP作為OO的高層原則,主張使用“抽象(Abstraction)”和“多型(Polymorphism)”將設計中的靜態結構改為動態結構,維持設計的封閉性“抽象”是語言提供的功能。“多型”由繼承語義實現。 如此,問題產生了:“我們如何去度量繼承關係的質量?” 
Liskov於1987年提出了一個關於繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確保超類所擁有的性質在子類中仍然成立。”也就是說,當一個子類的例項應該能夠替換任何其超類的例項時,它們之間才具有is-A關係。 
該原則稱為Liskov Substitution Principle——里氏替換原則。 
我們來研究一下LSP的實質。學習OO的時候,我們知道,一個物件是一組狀態和一系列行為的組合體。狀態是物件的內在特性,行為是物件的外在特性。LSP所表述的就是在同一個繼承體系中的物件應該有共同的行為特徵。 
這一點上,表明了OO的繼承與日常生活中的繼承的本質區別。舉一個例子:生物學的分類體系中把企鵝歸屬為鳥類。我們模仿這個體系,設計出這樣的類和關係。 


類“鳥”中有個方法fly,企鵝自然也繼承了這個方法,可是企鵝不能飛阿,於是,我們在企鵝的類中覆蓋了fly方法,告訴方法的呼叫者:企鵝是不會飛的。這完全符合常理。但是,這違反了LSP,企鵝是鳥的子類,可是企鵝卻不能飛!需要注意的是,此處的“鳥”已經不再是生物學中的鳥了,它是軟體中的一個類、一個抽象。 
有人會說,企鵝不能飛很正常啊,而且這樣編寫程式碼也能正常編譯,只要在使用這個類的客戶程式碼中加一句判斷就行了。但是,這就是問題所在!首先,客戶程式碼和“企鵝”的程式碼很有可能不是同時設計的,在當今軟體外包一層又一層的開發模式下,你甚至根本不知道兩個模組的原產地是哪裡,也就談不上去修改客戶程式碼了。客戶程式很可能是遺留系統的一部分,很可能已經不再維護,如果因為設計出這麼一個“企鵝”而導致必須修改客戶程式碼,誰應該承擔這部分責任呢?(大概是上帝吧,誰叫他讓“企鵝”不能飛的。^_^)“修改客戶程式碼”直接違反了OCP,這就是OCP的重要性。違反LSP將使既有的設計不能封閉! 

修正後的設計如下: 

LSP並沒有提供解決這個問題的方案,而只是提出了這麼一個問題。 於是,工程師們開始關注如何確保物件的行為。1988年,B. Meyer提出了Design by Contract(契約式設計)理論。DbC從形式化方法中借鑑了一套確保物件行為和自身狀態的方法,其基本概念很簡單: 

每個方法呼叫之前,該方法應該校驗傳入引數的正確性,只有正確才能執行該方法,否則認為呼叫方違反契約,不予執行。這稱為前置條件(Pre-condition)。 
一旦通過前置條件的校驗,方法必須執行,並且必須確保執行結果符合契約,這稱之為後置條件(Post-condition)。 
物件本身有一套對自身狀態進行校驗的檢查條件,以確保該物件的本質不發生改變,這稱之為不變式(Invariant)。
以上是單個物件的約束條件。為了滿足LSP,當存在繼承關係時,子類中方法的前置條件必須與超類中被覆蓋的方法的前置條件相同或者更寬鬆;而子類中方法的後置條件必須與超類中被覆蓋的方法的後置條件相同或者更為嚴格。 

4.DIP 依賴倒置原則 
依賴倒置(Dependence Inversion Principle)原則講的是:要依賴於抽象,不要依賴於具體。 
簡單的說,依賴倒置原則要求客戶端依賴於抽象耦合。原則表述: 
抽象不應當依賴於細節;細節應當依賴於抽象; 
要針對介面程式設計,不針對實現程式設計。 

5.ISP 介面隔離原則 
使用多個專門的介面比使用單一的總介面要好。廣義的介面:一個介面相當於劇本中的一種角色,而此角色在一個舞臺上由哪一個演員來演則相當於介面的實現。因此一個介面應當簡單的代表一個角色,而不是一個角色。,如果系統設計多個角色的話,則應當每一個角色都由一個特定的介面代表。狹義的介面(Interface):介面隔離原則講的就是同一個角色提供寬、窄不同的介面,以對付不同的客戶端。