c++ 面向?qū)ο笤O(shè)計(jì)五大原則
面向?qū)ο笤O(shè)計(jì)(OOD)是面向?qū)ο缶幊蹋∣OP)必不可少的一個(gè)環(huán)節(jié),只有好的設(shè)計(jì),才能保障程序的質(zhì)量。面向?qū)ο笤O(shè)計(jì)的主要任務(wù)就是類(lèi)的設(shè)計(jì),不少面向?qū)ο螅∣O)的先驅(qū)和前輩已經(jīng)提出了很多關(guān)于類(lèi)的設(shè)計(jì)原則,用于指導(dǎo)OOP,其中就包括類(lèi)設(shè)計(jì)的五項(xiàng)基本原則。
1.單一職責(zé)原則(Single Resposibility Principle,SRP)
專(zhuān)注是一個(gè)人的優(yōu)良品質(zhì),同樣,單一職責(zé)也是一個(gè)類(lèi)的優(yōu)良設(shè)計(jì)。單一職責(zé)的核心思想:一個(gè)類(lèi)只做好一件事情。
單一職責(zé)原則可以看作是高內(nèi)聚、低耦合在面向?qū)ο笤瓌t上的引申。類(lèi)的職責(zé)過(guò)多,容易導(dǎo)致類(lèi)間職責(zé)依賴(lài),提高耦合度,降低內(nèi)聚性。通常意義下的單一職責(zé),指的是類(lèi)只有一種單一功能,不要為類(lèi)設(shè)計(jì)過(guò)多的功能,交雜不清的功能會(huì)使代碼雜亂,提高程序開(kāi)發(fā)的難度和系統(tǒng)出錯(cuò)的概率,降低系統(tǒng)的可維護(hù)性。
要舉個(gè)體現(xiàn)單一職責(zé)原則的最常見(jiàn)的例子無(wú)疑就是STL中的迭代器的設(shè)計(jì)。有些人覺(jué)得容器跟迭代器的分離是不好的設(shè)計(jì),覺(jué)得增加了復(fù)雜度,不如直接把迭代器放在容器里更為簡(jiǎn)潔。不過(guò)很多人還是不這樣認(rèn)為,因?yàn)轭?lèi)的數(shù)量越多并不代表就越復(fù)雜,另外迭代器如果放到容器里面,就會(huì)暴露容器的一些內(nèi)部結(jié)構(gòu),不太符合封裝的思想。還有就是可擴(kuò)展性的問(wèn)題,因?yàn)閷?duì)容器的訪問(wèn)遍歷會(huì)有多種需求,如果把迭代器隔離開(kāi)來(lái)你可以不修改容器類(lèi),再定義些特制的迭代器就行了,這樣不管有什么奇怪的需求只要寫(xiě)個(gè)對(duì)應(yīng)的迭代器出來(lái)就行了。
2.開(kāi)放封閉原則(Open Closed Principle,OCP)
開(kāi)閉原則指的是開(kāi)放封閉原則,即對(duì)擴(kuò)展開(kāi)放,對(duì)修改封閉。
所謂修改封閉,就是之前設(shè)計(jì)好的類(lèi),不要去修改。比如刪除掉一個(gè)成員函數(shù)、改變成員函數(shù)的形參列表或更改數(shù)據(jù)成員類(lèi)型等。實(shí)現(xiàn)對(duì)修改封閉,關(guān)鍵在于抽象化。對(duì)一個(gè)事物抽象化,實(shí)質(zhì)上是對(duì)一個(gè)事物進(jìn)行概括、歸納、總結(jié),將其本質(zhì)特征抽象地用一個(gè)類(lèi)來(lái)表示,這樣類(lèi)才會(huì)相對(duì)穩(wěn)定,無(wú)需更改。
所謂擴(kuò)展開(kāi)放,就是在不改變已存在的類(lèi)的前提下可以添加很多功能。一般是通過(guò)繼承和多態(tài)來(lái)實(shí)現(xiàn),如此一來(lái),可以保持父類(lèi)的原樣,只需在子類(lèi)中添加些所需的新功能。
“需求總是變化的”,如果遵循開(kāi)放封閉原則,合理設(shè)計(jì)就能封閉變化,使類(lèi)能夠靈活的擴(kuò)展所需的功能。
3.里氏替換原則(Liskov Substituion Principle,LSP)
Liskov替換原則指的是:子類(lèi)可以替換父類(lèi)并出現(xiàn)在父類(lèi)能夠出現(xiàn)的任何地方。這個(gè)原則是Liskov于1987年提出,它同樣可以從Bertrand Meyer的DBC(Design by Contract,按契約設(shè)計(jì))的概念推出。
C++語(yǔ)言機(jī)制將類(lèi)的抽象與多態(tài)建立在繼承的基礎(chǔ)上,其實(shí)現(xiàn)的方法是面向接口編程:通過(guò)提取純虛類(lèi)(Abstract Class),將公共部分抽象為基類(lèi)接口或由子類(lèi)重寫(xiě)覆蓋基類(lèi)方法來(lái)達(dá)到多態(tài)的目的。Liskov替換原則的作用就是為了保證繼承復(fù)用的可靠。
下面來(lái)舉個(gè)違反替換原則的特殊例子:
正方形與長(zhǎng)方形的問(wèn)題也是屬于“圓不是橢圓”這類(lèi)問(wèn)題。我們知道正方形是一個(gè)特殊的長(zhǎng)方形,所以可以設(shè)計(jì)兩個(gè)類(lèi),正方形類(lèi)繼承自長(zhǎng)方形類(lèi)。長(zhǎng)方形類(lèi)有兩個(gè)成員變量,分別表示長(zhǎng)和寬,有個(gè)計(jì)算面積的成員函數(shù)。假如計(jì)算面積的方法是virtual的,這樣能實(shí)現(xiàn)多態(tài)。在先設(shè)定長(zhǎng)和寬后再調(diào)用計(jì)算面積的方法。我們知道正方形是長(zhǎng)和寬相等的,如果設(shè)定長(zhǎng)和寬的時(shí)候不是一樣的,然后調(diào)用了正方形的面積計(jì)算公式,這樣肯定就錯(cuò)了。你可能會(huì)問(wèn)咋這么扯蛋啊,為啥把長(zhǎng)和寬設(shè)成不一樣啊。很多設(shè)計(jì)思想和方法是一來(lái)為了方便,二來(lái)為了讓用戶(hù)少犯錯(cuò)誤,就是不管你怎么使用都不會(huì)出錯(cuò),要出錯(cuò)應(yīng)該是在編譯時(shí)出錯(cuò),放置運(yùn)行時(shí)出錯(cuò)。如果出現(xiàn)上面說(shuō)的情況編譯器是沒(méi)法讓你知道出錯(cuò)了的。
所以一個(gè)正方形類(lèi)繼承自長(zhǎng)方形類(lèi)的設(shè)計(jì)是不好的(注意的一點(diǎn)是你違反了Liskov替換原則并不是說(shuō)就寫(xiě)的代碼就會(huì)出錯(cuò),只是說(shuō)設(shè)計(jì)不太合理。實(shí)際上你這樣設(shè)計(jì)代碼沒(méi)準(zhǔn)可以正常的跑得很好呢,如果沒(méi)有出現(xiàn)一些特殊情況可能是一點(diǎn)bug也沒(méi)有,只不過(guò)設(shè)計(jì)不合理為導(dǎo)致一些安全隱患而已)。
4. 依賴(lài)倒置原則(Dependecy Inversion Principle,DIP)
其核心思想是:依賴(lài)于抽象。具體而言就是高層模塊不依賴(lài)于底層模塊,二者都依賴(lài)于抽象;抽象不依賴(lài)于具體,具體依賴(lài)于抽象。依賴(lài)倒置原則是對(duì)傳統(tǒng)過(guò)程性設(shè)計(jì)方法的“倒轉(zhuǎn)”,是高層次模塊復(fù)用及其可維護(hù)性的有效規(guī)范。
依賴(lài)一定存在于類(lèi)與類(lèi)、模塊與模塊之間。類(lèi)與類(lèi)之間產(chǎn)生依賴(lài)時(shí),依賴(lài)倒置原則的理解可以描述如下:依賴(lài)就是剛開(kāi)始時(shí)具體細(xì)節(jié)間互相依賴(lài),我們將實(shí)現(xiàn)的細(xì)節(jié)變成抽象類(lèi),降低類(lèi)間耦合度。然后有了抽象類(lèi),繼承自它的實(shí)現(xiàn)類(lèi)也要依賴(lài)它。那倒置兩字咋理解呢? 一般情況我們是先關(guān)注細(xì)節(jié),然后根據(jù)細(xì)節(jié)抽象出來(lái)一些概括的方法,所以按常理一般是抽象要依賴(lài)于細(xì)節(jié)的,而現(xiàn)在是是倒過(guò)來(lái)了,確定一個(gè)抽象類(lèi)后,那些細(xì)節(jié)的實(shí)現(xiàn)得以抽象出來(lái)的方法為基準(zhǔn),變成了細(xì)節(jié)依賴(lài)于抽象了,不然你要繼承了一個(gè)抽象類(lèi),你不完全實(shí)現(xiàn)它的方法的話可不讓你實(shí)例化對(duì)象的啊。
當(dāng)兩個(gè)模塊之間存在緊密的耦合關(guān)系時(shí),最好的方法就是分離接口和實(shí)現(xiàn):在依賴(lài)之間定義一個(gè)抽象的接口,供高層模塊調(diào)用,底層模塊實(shí)現(xiàn)接口的定義,從而有效控制耦合關(guān)系,達(dá)到依賴(lài)于抽象的設(shè)計(jì)目的。
依賴(lài)于抽象就是不對(duì)實(shí)現(xiàn)編程,而對(duì)接口編程。依賴(lài)于抽象是一個(gè)通用原則,而有些時(shí)候依賴(lài)于細(xì)節(jié)是在所難免的,我們需要根據(jù)具體情況在在抽象與具體之間進(jìn)行取舍。
5.接口分離原則(Interface Segregation Principle,ISP)
該原則的核心思想是:使用多個(gè)小的專(zhuān)門(mén)的接口,而不要使用一個(gè)大的總接口。具體而言,接口應(yīng)該是內(nèi)聚的,應(yīng)該避免“胖”接口。一個(gè)類(lèi)對(duì)另一個(gè)類(lèi)的依賴(lài)應(yīng)該建立在最小的接口上,而不要強(qiáng)迫依賴(lài)不同的方法,這是一種接口污染。
其實(shí)簡(jiǎn)單點(diǎn)的講與前面說(shuō)的單一職責(zé)類(lèi)似,這里的接口不是函數(shù)接口,而是一個(gè)類(lèi)。C#中的有專(zhuān)門(mén)的接口interface,和類(lèi)區(qū)分開(kāi)來(lái),而且C#中不像C++支持類(lèi)的多繼承,只支持接口的多繼承,所以這里可以把接口理解成功能更小更特殊的類(lèi),一個(gè)接口可能就只要那么幾個(gè)很少的方法就OK了。
接口分離手段主要有以下兩種方式:
(1)利用委托分離接口;
(2)利用多重繼承分離接口。
6.小結(jié)
概括地講,面向?qū)ο笤O(shè)計(jì)原則仍然是面向?qū)ο笏枷氲捏w現(xiàn)。例如,
(1)單一職責(zé)原則要求類(lèi)只負(fù)責(zé)一件事情。接口分離原則,讓客戶(hù)只關(guān)心他們所需的接口。單一職責(zé)原則與接口分離原都體現(xiàn)了內(nèi)聚的思想;
(2)開(kāi)放封閉原則,要求類(lèi)不作修改而能夠擴(kuò)展功能,體現(xiàn)了類(lèi)的封裝與繼承;
(3)Liskov替換原則,要求派生類(lèi)要能夠替換基類(lèi),是對(duì)類(lèi)繼承的規(guī)范;
(4)依賴(lài)倒置原則,要求類(lèi)依賴(lài)于抽象,而不是實(shí)現(xiàn),是抽象思想的體現(xiàn)。
上面五條面向?qū)ο笤O(shè)計(jì)原則,可以幫助我們?cè)O(shè)計(jì)出代碼易于復(fù)用、功能易于擴(kuò)展、運(yùn)營(yíng)易于維護(hù)的程序,需要我們?cè)趯?shí)踐中遵守。
以上就是c++ 面向?qū)ο笤O(shè)計(jì)五大原則的詳細(xì)內(nèi)容,更多關(guān)于c++ 面向?qū)ο笤O(shè)計(jì)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于C++實(shí)現(xiàn)簡(jiǎn)單日期計(jì)算器
這篇文章主要介紹了基于C++實(shí)現(xiàn)簡(jiǎn)單日期計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05C++中的三種繼承public,protected,private詳細(xì)解析
我們已經(jīng)知道,在基類(lèi)以private方式被繼承時(shí),其public和protected成員在子類(lèi)中變?yōu)閜rivate成員。然而某些情況下,需要在子類(lèi)中將一個(gè)或多個(gè)繼承的成員恢復(fù)其在基類(lèi)中的訪問(wèn)權(quán)限2013-09-09C/C++實(shí)現(xiàn)個(gè)人收支系統(tǒng)的示例代碼
這篇文章主要介紹了C/C++實(shí)現(xiàn)個(gè)人收支系統(tǒng),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06C語(yǔ)言實(shí)現(xiàn)數(shù)字連連看
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)數(shù)字連連看游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09C語(yǔ)言strlen和sizeof在數(shù)組中的使用詳解
對(duì)于 strlen 和 sizeof,相信不少程序員會(huì)混淆其功能。雖然從表面上看它們都可以求字符串的長(zhǎng)度,但二者卻存在著許多不同之處及本質(zhì)區(qū)別2021-10-10C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單彈球游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單彈球游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02C語(yǔ)言實(shí)現(xiàn)推箱子項(xiàng)目
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)推箱子項(xiàng)目,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08C語(yǔ)言進(jìn)階教程之循環(huán)語(yǔ)句缺陷詳析
循環(huán)語(yǔ)句是用于重復(fù)執(zhí)行某條語(yǔ)句(循環(huán)體)的語(yǔ)句,它包含一個(gè)控制表達(dá)式,每循環(huán)執(zhí)行一次都要對(duì)控制表達(dá)式進(jìn)行判斷,如果表達(dá)式為真,則繼續(xù)執(zhí)行循環(huán),這篇文章主要給大家介紹了關(guān)于C語(yǔ)言進(jìn)階教程之循環(huán)語(yǔ)句缺陷的相關(guān)資料,需要的朋友可以參考下2021-08-08