【思考】再谈面向过程与面向对象
【思考】再談面向過程與面向?qū)ο?
在我博客創(chuàng)作早期,寫了一篇博文,名字是【Java核心技術(shù)卷】面向過程與面向?qū)ο髮Ρ取?/p>
這篇文章試圖對比描述了關(guān)于 面向過程與面向?qū)ο?/strong> 的內(nèi)容。為什么還要再談呢?
一方面原因是深度不夠,另一方面原因要從對各種編程語言的感知說起 (涵蓋面向?qū)ο?、面向過程):
- 編譯執(zhí)行的C語言是靜態(tài)語言、弱類型語言。
- 解釋執(zhí)行的JavaScript語言是動態(tài)語言、弱類型語言。
- 混合編譯執(zhí)行的Java是靜態(tài)語言、強(qiáng)類型語言。
如果你不太明白靜態(tài)語言和動態(tài)語言以及強(qiáng)類型與弱類型,看文末的補(bǔ)充內(nèi)容。
似乎有很多獨(dú)特的“語言”,而且每一種語言背后都有非常深的“技術(shù)”蘊(yùn)含其中。
之前也曾就C語言,Java,Python,JavaScript這四種語言對比過它們的跨平臺能力,翻譯成機(jī)器碼執(zhí)行的過程,詳情參見【Java核心技術(shù)卷】面向?qū)ο笈c面向過程語言對比
為了幫助你復(fù)習(xí)一遍,這里僅僅展示文章里面的四張圖:
? C語言
? Java語言
? JavaScript語言
? Python語言
但是無論是 面向過程,還是面向?qū)ο?#xff0c; 肯定都有相通之處,也有區(qū)別所在。
面向過程就不多說,基本是C的天下了。
那么對于面向?qū)ο竽?#xff1f;
? 我們這里首先談?wù)撘幌旅嫦驅(qū)ο蟮南嗤ㄖ?#xff1a;
?
面向?qū)ο笥兄蠡咎卣?/p>
這個你就比較熟悉了。
封裝:
把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對象操作,對不可信的進(jìn)行信息隱藏。
類將成員變量和成員函數(shù)封裝在類的內(nèi)部,根據(jù)需要設(shè)置訪問權(quán)限,通過成員函數(shù)管理內(nèi)部狀態(tài)。
繼承:
繼承所表達(dá)的是類之間相關(guān)的關(guān)系,這種關(guān)系使得對象可以繼承另外一類對象的特征和能力。
繼承的作用:避免公用代碼的重復(fù)開發(fā),減少代碼和數(shù)據(jù)冗余。
多態(tài)
多態(tài)性可以簡單地概括為“一個接口,多種方法”,字面意思為多種形態(tài)。程序在運(yùn)行時才決定調(diào)用的函數(shù),它是面向?qū)ο缶幊填I(lǐng)域的核心概念。
但是插句題外話,你知道你所熟悉的語言的"繼承"與“多態(tài)”是如何實(shí)現(xiàn)的嘛?
關(guān)于Java的話可以參考這三篇文章
【Java核心技術(shù)卷】了解Java的內(nèi)存邏輯對象模型
【Java核心技術(shù)卷】理解Java的繼承與多態(tài)重要概念
【Java核心技術(shù)卷】深入理解Java的動態(tài)綁定,靜態(tài)綁定和多態(tài)
?
不知道你是否學(xué)過設(shè)計(jì)模式的相關(guān)內(nèi)容,像UML、七大軟件設(shè)計(jì)原則、二十三種設(shè)計(jì)模式 它們中有很多的東西都是面向?qū)ο笏ㄓ玫?#xff0c;里面深刻地體現(xiàn)著面向?qū)ο蟮乃枷搿?/p>
有一句話說的很好:“使用設(shè)計(jì)模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。 設(shè)計(jì)模式使代碼編寫真正工程化;設(shè)計(jì)模式是軟件工程的基石脈絡(luò),如同大廈的結(jié)構(gòu)一樣”
建議每個人都要好好琢磨琢磨。
?
那你聽說過面向?qū)ο蟮奈宕蠡驹瓌t嗎?
其實(shí)這五條也是七大軟件設(shè)計(jì)原則中的內(nèi)容,我們看吧~
七大軟件設(shè)計(jì)原則 可以參考【Java設(shè)計(jì)模式】軟件設(shè)計(jì)七大原則
實(shí)現(xiàn)語言是 Java哈,因?yàn)橛信e例所以更好理解一些。
面向?qū)ο蟮奈宕蠡驹瓌t 文字?jǐn)⑹霾糠?/h5>
這部分內(nèi)容參考了網(wǎng)上的資料,但是因?yàn)閬碓催^多,無法注明出處了。
一、 單一職責(zé)原則(Single-Resposibility Principle)
其核心思想為:一個類,最好只做一件事,只有一個引起它的變化。
單一職責(zé)原則可以看做是低耦合、高內(nèi)聚在面向?qū)ο笤瓌t上的引申,將職責(zé)定義為引起變化的原因,以提高內(nèi)聚性來減少引起變化的原因。
職責(zé)過多,可能引起它變化的原因就越多,這將導(dǎo)致職責(zé)依賴,相互之間就產(chǎn)生影響,從而大大損傷其內(nèi)聚性和耦合度。
通常意義下的單一職責(zé),就是指只有一種單一功能,不要為類實(shí)現(xiàn)過多的功能點(diǎn),以保證實(shí)體只有一個引起它變化的原因。
單一是一個類的優(yōu)良設(shè)計(jì)。交雜不清的職責(zé)將使得代碼看起來特別別扭牽一發(fā)而動全身,有失美感和必然導(dǎo)致丑陋的系統(tǒng)錯誤風(fēng)險(xiǎn)。
二、開放封閉原則(Open-Closed principle)
其核心思想是:軟件實(shí)體應(yīng)該是可擴(kuò)展的,而不可修改的。也就是,對擴(kuò)展開放,對修改封閉的。
開放封閉原則主要體現(xiàn)在兩個方面:
1、對擴(kuò)展開放,意味著有新的需求或變化時,可以對現(xiàn)有代碼進(jìn)行擴(kuò)展,以適應(yīng)新的情況。
2、對修改封閉,意味著類一旦設(shè)計(jì)完成,就可以獨(dú)立完成其工作,而不要對其進(jìn)行任何嘗試的修改。
實(shí)現(xiàn)開開放封閉原則的核心思想就是對抽象編程,而不對具體編程,因?yàn)槌橄笙鄬Ψ€(wěn)定。讓類依賴于固定的抽象,所以修改就是封閉的;而通過面向?qū)ο蟮睦^承和多態(tài)機(jī)制,又可以實(shí)現(xiàn)對抽象類的繼承,通過覆寫其方法來改變固有行為,實(shí)現(xiàn)新的拓展方法,所以就是開放的。
“需求總是變化”沒有不變的軟件,所以就需要用封閉開放原則來封閉變化滿足需求,同時還能保持軟件內(nèi)部的封裝體系穩(wěn)定,不被需求的變化影響。
三、Liskov替換原則(Liskov-Substituion Principle)
其核心思想是:子類必須能夠替換其基類。
這一思想體現(xiàn)為對繼承機(jī)制的約束規(guī)范,只有子類能夠替換基類時,才能保證系統(tǒng)在運(yùn)行期內(nèi)識別子類,這是保證繼承復(fù)用的基礎(chǔ)。
在父類和子類的具體行為中,必須嚴(yán)格把握繼承層次中的關(guān)系和特征,將基類替換為子類,程序的行為不會發(fā)生任何變化。同時,這一約束反過來則是不成立的,子類可以替換基類,但是基類不一定能替換子類。
Liskov替換原則,主要著眼于對抽象和多態(tài)建立在繼承的基礎(chǔ)上,因此只有遵循了Liskov替換原則,才能保證繼承復(fù)用是可靠地。
實(shí)現(xiàn)的方法是面向接口編程:將公共部分抽象為基類接口或抽象類,通過Extract Abstract Class,在子類中通過覆寫父類的方法實(shí)現(xiàn)新的方式支持同樣的職責(zé)。
Liskov替換原則是關(guān)于繼承機(jī)制的設(shè)計(jì)原則,違反了Liskov替換原則就必然導(dǎo)致違反開放封閉原則。
Liskov替換原則能夠保證系統(tǒng)具有良好的拓展性,同時實(shí)現(xiàn)基于多態(tài)的抽象機(jī)制,能夠減少代碼冗余,避免運(yùn)行期的類型判別。
四、 依賴倒置原則(Dependecy-Inversion Principle)
其核心思想是:依賴于抽象。具體而言就是高層模塊不依賴于底層模塊,二者都同依賴于抽象;抽象不依賴于具體,具體依賴于抽象。
我們知道,依賴一定會存在于類與類、模塊與模塊之間。當(dāng)兩個模塊之間存在緊密的耦合關(guān)系時,最好的方法就是分離接口和實(shí)現(xiàn):在依賴之間定義一個抽象的接口使得高層模塊調(diào)用接口,而底層模塊實(shí)現(xiàn)接口的定義,以此來有效控制耦合關(guān)系,達(dá)到依賴于抽象的設(shè)計(jì)目標(biāo)。
抽象的穩(wěn)定性決定了系統(tǒng)的穩(wěn)定性,因?yàn)槌橄笫遣蛔兊?#xff0c;依賴于抽象是面向?qū)ο笤O(shè)計(jì)的精髓,也是依賴倒置原則的核心。
依賴于抽象是一個通用的原則,而某些時候依賴于細(xì)節(jié)則是在所難免的,必須權(quán)衡在抽象和具體之間的取舍,方法不是一層不變的。依賴于抽象,就是對接口編程,不要對實(shí)現(xiàn)編程。
五、接口隔離原則(Interface-Segregation Principle)
其核心思想是:使用多個小的專門的接口,而不要使用一個大的總接口。
具體而言,接口隔離原則體現(xiàn)在:接口應(yīng)該是內(nèi)聚的,應(yīng)該避免“胖”接口。一個類對另外一個類的依賴應(yīng)該建立在最小的接口上,不要強(qiáng)迫依賴不用的方法,這是一種接口污染。
接口有效地將細(xì)節(jié)和抽象隔離,體現(xiàn)了對抽象編程的一切好處,接口隔離強(qiáng)調(diào)接口的單一性。而胖接口存在明顯的弊端,會導(dǎo)致實(shí)現(xiàn)的類型必須完全實(shí)現(xiàn)接口的所有方法、屬性等;而某些時候,實(shí)現(xiàn)類型并非需要所有的接口定義,在設(shè)計(jì)上這是“浪費(fèi)”,而且在實(shí)施上這會帶來潛在的問題,對胖接口的修改將導(dǎo)致一連串的客戶端程序需要修改,有時候這是一種災(zāi)難。在這種情況下,將胖接口分解為多個特點(diǎn)的定制化方法,使得客戶端僅僅依賴于它們的實(shí)際調(diào)用的方法,從而解除了客戶端不會依賴于它們不用的方法。
分離的手段主要有以下兩種:
1、委托分離,通過增加一個新的類型來委托客戶的請求,隔離客戶和接口的直接依賴,但是會增加系統(tǒng)的開銷。
2、多重繼承分離,通過接口多繼承來實(shí)現(xiàn)客戶的需求,這種方式是較好的。
以上就是5個基本的面向?qū)ο笤O(shè)計(jì)原則,它們就像面向?qū)ο蟪绦蛟O(shè)計(jì)中的金科玉律,遵守它們可以使我們的代碼更加鮮活,易于復(fù)用,易于拓展,靈活優(yōu)雅。不同的設(shè)計(jì)模式對應(yīng)不同的需求,而設(shè)計(jì)原則則代表永恒的靈魂,需要在實(shí)踐中時時刻刻地遵守。就如ARTHUR J.RIEL在那邊《OOD啟示錄》中所說的:“你并不必嚴(yán)格遵守這些原則,違背它們也不會被處以宗教刑罰。但你應(yīng)當(dāng)把這些原則看做警鈴,若違背了其中的一條,那么警鈴就會響起?!?/p>
為了讓代碼更加完美,我們往往會重構(gòu)它,如果能夠很好遵守這5個基本的面向?qū)ο笤O(shè)計(jì)原則,并且有著良好的單元測試習(xí)慣,那么重構(gòu)將不會一下子變得無比艱難。
? 那面向?qū)ο蟮恼Z言的區(qū)別呢?
這范圍可就廣了,用我熟悉的Java和C++說一下吧,通過對比,我們是能夠?qū)W到東西的:
?
C++ 被設(shè)計(jì)成主要用在系統(tǒng)性應(yīng)用程序設(shè)計(jì)上的語言,對C語言進(jìn)行了擴(kuò)展。對于C語言, C++ 特別加上了以下這些特性的支持:靜態(tài)類型的面向?qū)ο蟪绦蛟O(shè)計(jì)的支持、異常處理、RAII以及泛型。另外它還加上了一個包含泛型容器和算法的C++庫函數(shù)。
Java 依賴一個虛擬機(jī)來保證安全和可移植性。Java包含一個可擴(kuò)展的庫用以提供一個完整的的下層平臺的抽象。Java是一種靜態(tài)面向?qū)ο笳Z言,它使用的語法類似C++,但與之不兼容。為了使更多的人到使用更易用的語言,它進(jìn)行了全新的設(shè)計(jì)。
?
C++是編譯型語言(首先將源代碼編譯生成機(jī)器語言,再由機(jī)器運(yùn)行機(jī)器碼),執(zhí)行速度快、效率高;依賴編譯器、跨平臺性差些。
Java是混合型語言(源代碼不是直接翻譯成機(jī)器語言,而是先翻譯成中間代碼,再由解釋器對中間代碼進(jìn)行解釋運(yùn)行。),執(zhí)行速度慢、效率低;依賴解釋器、跨平臺性好。
?
C++是平臺相關(guān)的
Java是平臺無關(guān)的。
?
C++對所有的數(shù)字類型有標(biāo)準(zhǔn)的范圍限制,但字節(jié)長度是跟具體實(shí)現(xiàn)相關(guān)的,不同操作系統(tǒng)可能。
Java在所有平臺上對所有的基本類型都有標(biāo)準(zhǔn)的范圍限制和字節(jié)長度。
?
C++除了一些比較少見的情況之外和C語言兼容 。
Java沒有對任何之前的語言向前兼容。但在語法上受 C/C++ 的影響很大
?
C++允許直接調(diào)用本地的系統(tǒng)庫 。
Java要通過JNI調(diào)用, 或者 JNA
?
C++允許過程式程序設(shè)計(jì)和面向?qū)ο蟪绦蛟O(shè)計(jì) 。
Java必須使用面向?qū)ο蟮某绦蛟O(shè)計(jì)方式
?
C++支持指針,引用,傳值調(diào)用 。
Java只有值傳遞。
Java只有值傳遞 , 這個 你不好奇嗎?
?
C++需要顯式的內(nèi)存管理,但有第三方的框架可以提供垃圾搜集的支持。支持析構(gòu)函數(shù)。
Java 是自動垃圾收集的。沒有析構(gòu)函數(shù)的概念。
?
C++支持多重繼承,包括虛擬繼承 。
Java只允許單繼承,需要多繼承的情況要使用接口。
千萬不要把自己限制死了,通過比較能拓寬我們的見識。
最后補(bǔ)充一下上面需要參考的內(nèi)容:
靜態(tài)類型語言、動態(tài)類型語言分析:
靜態(tài)類型語言:變量定義時有類型聲明的語言。
1)變量的類型在編譯的時候確定
2)變量的類型在運(yùn)行時不能修改
這樣編譯器就可以確定運(yùn)行時需要的內(nèi)存總量。
例如:C/C++/Java/C#語言是靜態(tài)類型語言。
動態(tài)類型語言:變量定義時無類型聲明的語言。
1)變量的類型在運(yùn)行的時候確定
2)變量的類型在運(yùn)行可以修改
例如:Javascript語言是動態(tài)類型語言。
由于動態(tài)類型和靜態(tài)類型語言的特性衍生出強(qiáng)類型語言和弱類型、無類型語言。
強(qiáng)類型語言、弱類型、無類型語言:
弱/強(qiáng)類型指的是語言類型系統(tǒng)的類型檢查的嚴(yán)格程度。弱類型相對于強(qiáng)類型來說類型檢查更不嚴(yán)格,比如說允許變量類型的隱式轉(zhuǎn)換,允許強(qiáng)制類型轉(zhuǎn)換等等。
- 強(qiáng)類型語言:例如Java/C#語言是強(qiáng)類型語言,強(qiáng)類型定義語言是類型安全的語言,是由編譯器以及編譯器生成的中間代碼來保證類型安全。
- 弱類型語言:C/C++/Javascript語言是弱類型語言,其類型安全由程序員來保證,Javascript語言的安全由程序員來保證。
無類型語言:是動態(tài)語言,變量中既可以存放數(shù)據(jù)又可以存放代碼。
總結(jié)
以上是生活随笔為你收集整理的【思考】再谈面向过程与面向对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于树莓派(二):测试树莓派pi-cam
- 下一篇: 使用 office 365 SMTP 发