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