c++ 面向物件設計五大原則
面向物件設計(OOD)是面向物件程式設計(OOP)必不可少的一個環節,只有好的設計,才能保障程式的質量。面向物件設計的主要任務就是類的設計,不少面向物件(OO)的先驅和前輩已經提出了很多關於類的設計原則,用於指導OOP,其中就包括類設計的五項基本原則。
1.單一職責原則(Single Resposibility Principle,SRP)
專注是一個人的優良品質,同樣,單一職責也是一個類的優良設計。單一職責的核心思想:一個類只做好一件事情。
單一職責原則可以看作是高內聚、低耦合在面向物件原則上的引申。類的職責過多,容易導致類間職責依賴,提高耦合度,降低內聚性。通常意義下的單一職責,指的是類只有一種單一功能,不要為類設計過多的功能,交雜不清的功能會使程式碼雜亂,提高程式開發的難度和系統出錯的概率,降低系統的可維護性。
要舉個體現單一職責原則的最常見的例子無疑就是STL中的迭代器的設計。有些人覺得容器跟迭代器的分離是不好的設計,覺得增加了複雜度,不如直接把迭代器放在容器裡更為簡潔。不過很多人還是不這樣認為,因為類的數量越多並不代表就越複雜,另外迭代器如果放到容器裡面,就會暴露容器的一些內部結構,不太符合封裝的思想。還有就是可擴充套件性的問題,因為對容器的訪問遍歷會有多種需求,如果把迭代器隔離開來你可以不修改容器類,再定義些特製的迭代器就行了,這樣不管有什麼奇怪的需求只要寫個對應的迭代器出來就行了。
2.開放封閉原則(Open Closed Principle,OCP)
開閉原則指的是開放封閉原則,即對擴充套件開放,對修改封閉。
所謂修改封閉,就是之前設計好的類,不要去修改。比如刪除掉一個成員函式、改變成員函式的形參列表或更改資料成員型別等。實現對修改封閉,關鍵在於抽象化。對一個事物抽象化,實質上是對一個事物進行概括、歸納、總結,將其本質特徵抽象地用一個類來表示,這樣類才會相對穩定,無需更改。
所謂擴充套件開放,就是在不改變已存在的類的前提下可以新增很多功能。一般是通過繼承和多型來實現,如此一來,可以保持父類的原樣,只需在子類中新增些所需的新功能。
“需求總是變化的”,如果遵循開放封閉原則,合理設計就能封閉變化,使類能夠靈活的擴充套件所需的功能。
3.里氏替換原則(Liskov Substituion Principle,LSP)
Liskov替換原則指的是:子類可以替換父類並出現在父類能夠出現的任何地方。這個原則是Liskov於1987年提出,它同樣可以從Bertrand Meyer的DBC(Design by Contract,按契約設計)的概念推出。
C++語言機制將類的抽象與多型建立在繼承的基礎上,其實現的方法是面向介面程式設計:通過提取純虛類(Abstract Class),將公共部分抽象為基類介面或由子類重寫覆蓋基類方法來達到多型的目的。Liskov替換原則的作用就是為了保證繼承複用的可靠。
下面來舉個違反替換原則的特殊例子:
正方形與長方形的問題也是屬於“圓不是橢圓”這類問題。我們知道正方形是一個特殊的長方形,所以可以設計兩個類,正方形類繼承自長方形類。長方形類有兩個成員變數,分別表示長和寬,有個計算面積的成員函式。假如計算面積的方法是virtual的,這樣能實現多型。在先設定長和寬後再呼叫計算面積的方法。我們知道正方形是長和寬相等的,如果設定長和寬的時候不是一樣的,然後呼叫了正方形的面積計算公式,這樣肯定就錯了。你可能會問咋這麼扯蛋啊,為啥把長和寬設成不一樣啊。很多設計思想和方法是一來為了方便,二來為了讓使用者少犯錯誤,就是不管你怎麼使用都不會出錯,要出錯應該是在編譯時出錯,放置執行時出錯。如果出現上面說的情況編譯器是沒法讓你知道出錯了的。
所以一個正方形類繼承自長方形類的設計是不好的(注意的一點是你違反了Liskov替換原則並不是說就寫的程式碼就會出錯,只是說設計不太合理。實際上你這樣設計程式碼沒準可以正常的跑得很好呢,如果沒有出現一些特殊情況可能是一點bug也沒有,只不過設計不合理為導致一些安全隱患而已)。
4. 依賴倒置原則(Dependecy Inversion Principle,DIP)
其核心思想是:依賴於抽象。具體而言就是高層模組不依賴於底層模組,二者都依賴於抽象;抽象不依賴於具體,具體依賴於抽象。依賴倒置原則是對傳統過程性設計方法的“倒轉”,是高層次模組複用及其可維護性的有效規範。
依賴一定存在於類與類、模組與模組之間。類與類之間產生依賴時,依賴倒置原則的理解可以描述如下:依賴就是剛開始時具體細節間互相依賴,我們將實現的細節變成抽象類,降低類間耦合度。然後有了抽象類,繼承自它的實現類也要依賴它。那倒置兩字咋理解呢? 一般情況我們是先關注細節,然後根據細節抽象出來一些概括的方法,所以按常理一般是抽象要依賴於細節的,而現在是是倒過來了,確定一個抽象類後,那些細節的實現得以抽象出來的方法為基準,變成了細節依賴於抽象了,不然你要繼承了一個抽象類,你不完全實現它的方法的話可不讓你例項化物件的啊。
當兩個模組之間存在緊密的耦合關係時,最好的方法就是分離介面和實現:在依賴之間定義一個抽象的介面,供高層模組呼叫,底層模組實現介面的定義,從而有效控制耦合關係,達到依賴於抽象的設計目的。
依賴於抽象就是不對實現程式設計,而對介面程式設計。依賴於抽象是一個通用原則,而有些時候依賴於細節是在所難免的,我們需要根據具體情況在在抽象與具體之間進行取捨。
5.介面分離原則(Interface Segregation Principle,ISP)
該原則的核心思想是:使用多個小的專門的介面,而不要使用一個大的總介面。具體而言,介面應該是內聚的,應該避免“胖”介面。一個類對另一個類的依賴應該建立在最小的介面上,而不要強迫依賴不同的方法,這是一種介面汙染。
其實簡單點的講與前面說的單一職責類似,這裡的介面不是函式介面,而是一個類。C#中的有專門的介面interface,和類區分開來,而且C#中不像C++支援類的多繼承,只支援介面的多繼承,所以這裡可以把介面理解成功能更小更特殊的類,一個介面可能就只要那麼幾個很少的方法就OK了。
介面分離手段主要有以下兩種方式:
(1)利用委託分離介面;
(2)利用多重繼承分離介面。
6.小結
概括地講,面向物件設計原則仍然是面向物件思想的體現。例如,
(1)單一職責原則要求類只負責一件事情。介面分離原則,讓客戶只關心他們所需的介面。單一職責原則與介面分離原都體現了內聚的思想;
(2)開放封閉原則,要求類不作修改而能夠擴充套件功能,體現了類的封裝與繼承;
(3)Liskov替換原則,要求派生類要能夠替換基類,是對類繼承的規範;
(4)依賴倒置原則,要求類依賴於抽象,而不是實現,是抽象思想的體現。
上面五條面向物件設計原則,可以幫助我們設計出程式碼易於複用、功能易於擴充套件、運營易於維護的程式,需要我們在實踐中遵守。
以上就是c++ 面向物件設計五大原則的詳細內容,更多關於c++ 面向物件設計的資料請關注我們其它相關文章!