面向對象和面向過程分別是什麼?

我才學c和c#,發現了這些奇怪的術語,一直沒懂,給小白科普下吧。


面向過程是編年體;面向對象是紀傳體


面向過程就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。面向對象是把構成問題事務分解成各個對象,建立對象的目的不是為了完成一個步驟,而是為了描敘某個事物在整個解決問題的步驟中的行為。

例如五子棋,面向過程的設計思路就是首先分析問題的步驟:

1、開始遊戲,2、黑子先走,3、繪製畫面,4、判斷輸贏,5、輪到白子,6、繪製畫面,7、判斷輸贏,8、返回步驟2,9、輸出最後結果。

把上面每個步驟用分別的函數來實現,問題就解決了。

而面向對象的設計則是從另外的思路來解決問題。整個五子棋可以分為:

1、黑白雙方,這兩方的行為是一模一樣的,2、棋盤系統,負責繪製畫面,3、規則系統,負責判定諸如犯規、輸贏等。

第一類對象(玩家對象)負責接受用戶輸入,並告知第二類對象(棋盤對象)棋子布局的變化,棋盤對象接收到了棋子的變化就要負責在屏幕上面顯示出這種變化,同時利用第三類對象(規則系統)來對棋局進行判定。


那些年搞不懂的高深術語——依賴倒置?控制反轉?依賴注入?面向介面編程

那些年,空氣中彷彿還能聞到漢唐盛世的餘韻,因此你決不允許自己的臉上有油光,時刻保持活力。然而,你一定曾為這些「高深術語」感到過困擾。也許時至今日,你仍對它們一知半解。不過就在今天,這一切都將徹底改變!我將帶領你以一種全新的高清視角進入奇妙的編程世界,領略涵泳在這些「高深術語」中的活潑潑的地氣,以及翩躚於青萍之末的雲水禪心。

·內聚

內聚,通俗的來講,就是自己的東西自己保管,自己的事情自己做。

經典理論告訴我們,程序的兩大要素:一個是數據(data),一個是操作(opration)。而 PASCAL之父Nicklaus Wirth則進一步提出了「程序 = 數據結構 + 演算法」的著名公式。雖然提法上有所差異,但是其根本內涵卻是一致的,微妙的差別在於,「數據 + 操作」是微觀的視域,「數據結構 + 演算法」則是中觀的視域。而在宏觀的視域下,我認為「程序 = 對象 + 消息」。對象是什麼?對象就是保管好自己的東西,做好自己的事情的程序模塊——這就是內聚!傳統的面向過程編程方法由於割裂了數據結構和演算法,使得軟體的內聚性普遍低迷,曾一度引發了軟體危機。試想,大家都自己的東西不好好保管,自己的事情也不好好做,不引發危機才怪呢!當然,對象的內聚只是內聚的一個層次,在不同的尺度下其實都有內聚的要求,比如方法也要講內聚,架構也要講內聚。

《周易·彖傳》中講「乾道變化,各正性命,保合太和,乃利貞」,就是要求每一個個體因循著各自的稟賦而努力成就各自的品性,然後各自保全,彼此和合,最終達成宇宙的完滿狀態。《論語·憲問》中,子路問君子。子曰:「修己以敬。」曰:「如斯而已乎?」曰:「修己以安人」,更是明確的教導我們要不斷提高自身的內聚性,最大限度地減少給他人造成的麻煩,從而達到安人、安百姓、安天下的目標。我想,成長的過程就是一個不斷提升內聚的過程。「自己的東西自己保管,自己的事情自己做」,這些孩提時代的教誨,放到今天仍能讓不少「大人」臉紅不已。太多的人保管不好自己的「東西」,保管不好自己的身體,保管不好自己的婚姻,更保管不好自己如蛛絲般震顫飄蕩的狂亂的心。至於做好自己的事情,則更是惘然,甚至很多人連自己的事情是什麼都搞不清楚,因此渾渾噩噩,飽食終日。內聚,是一個值得我們好好反思的問題。

·依賴·耦合

在面向對象編程中,對象自身是內聚的,是保管好自己的數據,完成好自己的操作的,而對外界呈現出自己的狀態和行為。但是,沒有絕對的自力更生,對外開放也是必要的!一個對象,往往需要跟其他對象打交道,既包括獲知其他對象的狀態,也包括仰賴其他對象的行為,而一旦這樣的事情發生時,我們便稱該對象依賴於另一對象。只要兩個對象之間存在一方依賴一方的關係,那麼我們就稱這兩個對象之間存在耦合。 比如媽媽和baby,媽媽要隨時關注baby的睡、醒、困、哭、尿等等狀態,baby則要仰賴媽媽的餵奶、哄睡、換紙尿褲等行為,從程序的意義上說,二者互相依賴,因此也存在耦合。首先要說,耦合是必要的。我們來看以下這個實驗。

【王陽明與山中之花

View Code

由於王陽明這個對象不依賴山花這個對象,又沒有其他的方式來獲知山花的盛開狀態,所以他要麼選擇不說,要麼瞎說,但不說編譯是通不過,而瞎說作為王陽明來講也是通不過的,所以這個系統是無法成立的。要想系統成立,必須要這樣寫:

public bool AdmireFlowers()
{
return flower.IsBloomed; ;
}

無論這個山花對象是怎麼來的,作為參數傳入還是作為屬性設置、還是在內部構造出來,總之,王陽明與山花之間發生了依賴,二者之間產生了耦合。 當然,這是一個很淺顯的問題。有趣的是王陽明對此事的看法:「你未看花時,花與你同寂;你來看花,花於你則一時分明起來。可見心外無物!」王陽明講的是對的!「心外無物」翻譯技術語言是這樣的:不存在耦合的兩個對象必然拿不到對方的引用!

·耦合度·解耦和

耦合的程度就是耦合度,也就是雙方依賴的程度。上文所說的媽媽和baby就是強耦合。而你跟快遞小哥之間則是弱耦合。一般來說耦合度過高並不是一件好事。就拿作為IT精英的你來說吧,上級隨時敦促你的工作進度,新手頻繁地需要你指導問題,隔三差五還需要參加酒局飯局,然後還要天天看領導的臉色、關注老婆的心情,然後你還要關注代碼中的bug 、bug、bug,和需求的變化、變化、變化,都夠焦頭爛額了,還猝不及防的要關注眼睛、頸椎、前列腺和頭髮的狀態,然後你再炒個股,這些加起來大概就是個強耦合了。從某種意義上來說,耦合天生就與自由為敵,無論是其他對象依賴於你,還是你依賴其他對象。比如有人嗜煙、酗酒,你有多依賴它們就有多不自由;比如有人家裡生了七八個娃,還有年邁的父母、岳父母,他們有多依賴你,你就有多不自由。所以老子這樣講:「五音令人耳聾,五色令人目盲,馳騁狩獵令人心發狂,難得之貨令人行妨。」盧梭也是不無悲涼的說「人生而自由,卻又無往而不在枷鎖中」。因此,要想自由,就必須要降低耦合,而這個過程就叫做解耦和。

·依賴倒置(Dependence Inversion Principle)

解耦和最重要的原則就是依賴倒置原則:

高層模塊不應該依賴底層模塊,他們都應該依賴抽象。抽象不應該依賴於細節,細節應該依賴於抽象。

《資本論》中都曾闡釋依賴倒轉原則——在商品經濟的萌芽時期,出現了物物交換。假設你要買一個IPhone,賣IPhone的老闆讓你拿一頭豬跟他換,可是你並沒有養豬,你只會編程。所以你找到一位養豬戶,說給他做一個養豬的APP來換他一頭豬,他說換豬可以,但是得用一條金項鏈來換——所以這裡就出現了一連串的對象依賴,從而造成了嚴重的耦合災難。解決這個問題的最好的辦法就是,買賣雙發都依賴於抽象——也就是貨幣——來進行交換,這樣一來耦合度就大為降低了。

再舉一個編程中的依賴倒置的例子。我們知道,在通信中,消息的收發和消息的處理往往密不可分。就一般的通信框架而言,消息的收發通常是已經實現了的,而消息的處理則是需要用戶來自定義完成的。先看一個正向依賴的例子:輕量級通信引擎StriveEngine。tcpServerEngine是StriveEngine.dll提供通信引擎,它發布有一個MessageReceived事件。假設我定義了一個CustomizeHandler類來用於消息處理,那麼CustomizeHandler的內部需要預定tcpServerEngine的MessageReceived事件,因此customizeHandler依賴於tcpServerEngine,這就是一個普通的依賴關係,也就是高層模塊依賴於低層模塊。

而ESFramework通信框架應用了依賴倒轉原則。ESFramework定義了一個IcustomizeHandler介面,用戶在進行消息處理時,實現該介面,然後將其注入到rapidPassiveEngine客戶端通信引擎之中。

View Code

很明顯,相比於上一個例子,這裡的依賴關係變成了rapidPassiveEngine依賴於customizeHandler,也就是說依賴關係倒置了過來,上層模塊不再依賴於底層模塊,而是它們共同依賴於抽象。rapidPassiveEngine依賴的是IcustomizeHandler介面類型的參數,customizeHandler同樣是以實現的介面的方式依賴於IcustomizeHandler——這就是一個依賴倒置的典範。

·控制反轉(Inversion of Control)

控制反轉跟依賴倒置是如出一轍的兩個概念,當存在依賴倒置的時候往往也存在著控制反轉。但是控制反轉也有自己的獨特內涵。

首先我們要區分兩個角色,server 跟 Client,也就是服務方和客戶方。提供服務端的一方稱為服務方,請求服務的一方稱為客戶方。我們最熟悉的例子就是分散式應用的C/S架構,服務端和客戶端。其實除此之外,C/S關係處處可見。比如在TCP/IP協議棧中,我們知道,每層協議為上一層提供服務,那麼這裡就是一個C/S關係。當我們使用開發框架時,開發框架就是作為服務方,而我們自己編寫的業務應用就是客戶方。當Client調用server時,這個叫做一般的控制;而當server調用Client時,就是我們所說的控制反轉,同時我們也將這個調用稱為「回調」。控制反轉跟依賴倒置都是一種編程思想,依賴倒置著眼於調用的形式,而控制反轉則著眼於程序流程的控制權。一般來說,程序的控制權屬於server,而一旦控制權交到Client,就叫控制反轉。比如你去下館子,你是Client餐館是server。你點菜,餐館負責做菜,程序流程的控制權屬於server;而如果你去自助餐廳,程序流程的控制權就轉到Client了,也就是控制反轉。

控制反轉的思想體現在諸多領域。比如事件的發布/ 訂閱就是一種控制反轉,GOF設計模式中也多處體現了控制反轉,比如典型的模板方法模式等。而開發框架則是控制反轉思想應用的集中體現。比如之前所舉的ESFramework通信框架的例子,通信引擎回調用戶自定義的消息處理器,這就是一個控制反轉。以及ESFramework回調用戶自定義的群組關係和好友關係,回調用戶自定義的用戶管理器以管理在線用戶相關狀態,回調用戶自定義的登陸驗證處理,等等不一而足。再比如與ESFramework一脈相承的輕量級通信引擎StriveEngine,通過回調用戶自定義的通信協議來實現更加靈活的通信。

由此我們也可以總結出開發框架與類庫的區別:使用開發框架時,框架掌握程序流程的控制權,而使用類庫時,則是應用程序掌握程序流程的控制權。或者說,使用框架時,程序的主循環位於框架中,而使用類庫時,程序的主循環位於應用程序之中。框架會回調應用程序,而類庫則不會回調應用程序。ESFramework和StriveEngine中最主要的對象都以engine來命名,我們也可以看出框架對於程序主循環的控制——它會為你把握方向、眼看前方、輕鬆駕馭!

·依賴注入(Dependency Injection)

  依賴注入與依賴倒置、控制反轉的關係仍舊是一本萬殊。依賴注入,就其廣義而言,即是通過「注入」的方式,來獲得依賴。我們知道,A對象依賴於B對象,等價於A對象內部存在對B對象的「調用」,而前提是A對象內部拿到了B對象的引用。B對象的引用的來源無非有以下幾種:A對象內部創建(無論是作為欄位還是作為臨時變數)、構造器注入、屬性注入、方法注入。後面三種方式統稱為「依賴注入」,而第一種方式我也生造了一個名詞,稱為「依賴內生」,二者根本的差異即在於,我所依賴的對象的創建工作是否由我自己來完成。當然,這個是廣義的依賴注入的概念,而我們一般不會這樣來使用。我們通常使用的,是依賴注入的狹義的概念。不過,直接陳述其定義可能會過於詰屈聱牙,我們還是從具體的例子來看。

  比如OMCS網路語音視頻框架,它實現了多媒體設備(麥克風、攝像頭、桌面、電子白板)的採集、編碼、網路傳送、解碼、播放(或顯示)等相關的一整套流程,可以快速地開發出視頻聊天系統、視頻會議系統、遠程醫療系統、遠程教育系統、網路監控系統等等基於網路多媒體的應用系統。然而,OMCS直接支持的是通用的語音視頻設備,而在某些系統中,需要使用網路攝像頭或者特殊的視頻採集卡作為視頻源,或者其它的聲音採集設備作為音頻源,OMCS則提供了擴展介面——用戶自己實現這個擴展的介面,然後以「依賴注入」的方式將對象實例注入到OMCS中,從而完成對音、視頻設備的擴展。

「依賴注入」常常用於擴展,尤其是在開發框架的設計中。從某種意義上來說,任何開發框架,天生都是不完整的應用程序。因此,一個優秀的開發框架,不僅要讓開發者能夠重用這些久經考驗的的卓越的解決方案,也要讓開發者能夠向框架中插入自定義的業務邏輯,從而靈活自由地適應特定的業務場景的需要——也就是說要具備良好的可擴展性。比如上面提到的OMCS網路語音視頻框架可應用於音、視頻聊天系統、視頻會議系統、遠程醫療系統、遠程教育系統、網路監控系統等等基於網路多媒體的應用系統;以及ESFramework通信框架能夠應用於即時通訊系統,大型多人在線遊戲、在線網頁遊戲、文件傳送系統、數據採集系統、分散式OA系統等任何需要分散式通信的軟體系統中——這種良好的擴展性都與「依賴注入」的使用密不可分!

·面向介面編程

談到最後,「面向介面編程」已經是呼之欲出。無論是依賴倒置、控制反轉、還是依賴注入,都已經蘊含著「面向介面編程」的思想。面向介面,就意味著面向抽象。作為哲學範疇而言,規定性少稱為抽象,規定性多稱為具體。而介面,就是程序中的一種典型的「抽象」的形式。面向抽象,就意味著面向事物的本質規定性,擺脫感性雜多的牽絆,從而把握住「必然」——而這本身就意味著自由,因為自由就是對必然的認識。

也許以上的這段論述太過「哲學」,但是「一本之理」與「萬殊之理」本身就「體用不二」——總結來看,依賴倒置、控制反轉、依賴注入都圍繞著「解耦和」的問題,而同時自始至終又都是「面向介面編程」的方法——因此,「面向介面編程」天生就是「解耦和」的好辦法。由此也印證了從「抽象」到「自由」的這一段範疇的辯證衍化。

「面向對象」與「面向介面」並非兩種不同的方法學,「面向介面」其實是「面向對象」的內在要求,是其一部分內涵的集中表述。我們對於理想軟體的期待常被概括為「高內聚,低耦合」,這也是整個現代軟體開發方法學所追求的目標。面向對象方法學作為現代軟體開發方法學的代表,本身就蘊含著「高內聚,低耦合」的思想精髓,從這個意義上來說,「面向對象」這個表述更加側重於「高內聚」,「面向介面」的表述則更加側重於「低耦合」——不過是同一事物的不同側面罷了。

除此之外,我們也能從「面向介面編程」的思想中得到「世俗」的啟迪——《論語》裡面講,不患無位,患所以立;不患人之不己知,患其不能也——就是教導我們要面向「我有沒有的本事?」、「我有沒有能力?」這樣的介面,而不是面向「我有沒有搞到位子?」、「別人了不了解我?」這樣的具體。依我看,這是莫大的教誨!


面向對象和面向過程並不是編程的區別,而是設計的區別。

如果說面向過程就是強調一步一步,那對象里的method,也是一步一步寫,所以我不認同字面上去解釋面向過程。

面向對象其實就是三點,

封裝(encapsulation),

繼承(Inheritance),

多態(polymorphism)。

這不是我總結的,而是C++ Primer如果我沒記錯的話,第十五章?還是第幾章我不記得了,反正是全書第二次介紹OOP的章節開篇講的(第一次是第七章這個我記得),我覺得總結的很經典。

這三點的意思我就不啰嗦了。無非是,

封裝,一切皆class的原因,任何程序,都要封裝好,provide介面就行了,好用且安全。

繼承,書中給的一句話的解釋是,讓我們在設計相似的東西時更加方便。

多態,書中給的一句話的解釋是,我們在使用類似的東西的時候可以不用去思考它們微弱的不同。

基於這三點,OOP,至少我個人的理解,OOP僅此而已。我們關心的不是過程,而是介面,而介面來自對象,故名為面向對象。


算你走運了,我最近才發現這個東東:http://www.cs.albany.edu/~sdc/CSI500/Downloads/ProgrammingParadigmsVanRoyChapter.pdf

面向過程強調的是發給計算機的命令序列,對於編程讓機器工作這件事兒來說,是一種非常直接、自然的思維方式。

但是,N. Wirth 說過:程序 = 數據結構 + 演算法。面向過程的編程範式里,數據和操作數據的邏輯是分離的,這增加了編寫時的心智負擔。於是將狀態和行為封裝成一個實體的面象對象就發展起來了。


首先吐槽把實用類庫當面向對象的特點的,編程方便輪子多就是面向對象了嗎?面向對象編程就不需要一步步的來寫了嗎?面向對象編程就沒有演算法和數據結構了嗎?寫個hello world能說明什麼呢?我很懷疑答題者的工作量和學習理解程度。將來理解上有偏差,你們是要負責的!

面向對象和面向過程在問題抽象上有程度上的發展,面向過程的抽象是將實現一個功能的一組命令組合成為一個函數,這個函數就能實現這一個功能,是對功能實現的一種抽象,有了函數,我們就沒有必要重複寫很多的代碼(特殊地譬如遞歸,可通過循環替代),僅僅一個代碼集合名稱以及參數就能調用抽象的命令集合。要想擴充,或者是對同一功能的不同實現,那麼對不起,你需要重新寫一個函數,這就導致在功能層面是同類函數,卻在語言層面是完全不同的兩個函數,這就導致隨著代碼量的提升,寫程序會越來越難,手冊會越來越厚。

有了面向對象編程方法,它著重於繼承(使用「擴充」一詞或許更易於理解),把數據或方法統統使用「對象」的概念來統一起來,對象里可以存在狀態以及方法,這個「對象」,就應該是問題解決過程中的一個角色,因為繼承導致多態,所謂的父類,無非就是集合對象的代表,具體的對象具有特殊性,抽象的對象具有一般性。都說特殊問題特殊對待,我們用一般性的框架規定好上層的命令執行流程,下層特殊的情況讓特殊的對象參與進來進行解決。


過程關心的是先幹什麼(第一步),然後在幹什麼(第二步)。。。。。

對象關心的是有什麼(屬性),它可以幹什麼(方法),以及它正在發生什麼(事件)。

比如吃飯,我們喝湯,再裝飯,再裝菜,再大口的吃飯。這就是過程。

誰在吃飯,吃飯的時間 (這是屬性)

大口吃飯,還是小口吃飯(這是方法)

正在吃飯還是正在吃菜(這是事件)


面向過程是數據加演算法 ,數據和對數據的操作是分離的

面向對象是對象加消息,對象將數據和對數據的操作封裝在了一起,然後對象與對象之間通過消息進行「聯繫」,所以數據和對數據的操作是一起的。

粗略理解,請指正。。。


/*重新修改後的答案*/

從一個小菜比的角度來理解一下。

一個非常經典的對面向對象的概括「封裝 繼承 多態」

面向對象相比面向過程,對於我這種菜逼來說,首先感受到的就是將程序中的基本單元從函數變成了類/對象,類/對象下還有成員方法和成員數據。

相比面向過程,面向對象提供了一個新的層次去解析你的問題,這樣每一層需要考慮的東西相對變少了一點。

這大概就是大家說的面向對象的第一個特徵:「封裝」。

當然,封裝在coding中隨處可見,不應該僅僅當做面向對象的一個基本特徵。

繼承和多態不應該分開敘述,原因不贅述。

曾經我以為繼承是面向對象的基本特徵,直到我看到了Go;曾經我以為多態是面向對象的基本特徵,直到我看到了Python。

Go中取消了繼承,但是保留了介面;Python中雖然有繼承,但是由於有duck typing的存在,個人很少用到繼承。

小弟在C++中,認為多態就是允許將子類類型的指針賦值給父類類型的指針。

但是在Python這樣支持duck typing的語言中,連繼承都可以不用,又何來的這種狹義的多態

事實上,面向對象最初需要解決的問題,究竟是什麼?

是用更容易被人類理解的思路去利用代碼表達解決問題的方法。

面向對象的第一步,就是描述對象。

那麼讓我們想像一下,當我們向其他人描述一個他從未見過從未理解過的東西,我們該如何介紹?

「他的眼鏡很大,他的腰帶很高……」或者是「他長得像一個蛤蟆」。

是的,我們用一系列對方理解的東西組合了一個描述或者是用一個對方理解的東西概括。

前者我們一般叫組合,後者我們往往是利用繼承來實現的。

但很明顯,繼承絕不是完完全全的複製,那樣沒有任何意義。子類必然會在父類的基礎上進行修改。所以這個時候,我們可能就要說「他長得像一個蛤蟆,但是他的姿勢水平很高」。

那麼出現了什麼問題?

如果我們對整個系統理解充分、抽象合理,繼承這種「is a」的描述可以節約大量的「口水」,在編程時意味著減少代碼量、節約打字時間。

然而現實是,我們往往無法在面對一個陌生系統時,快速、準確的對他進行概括。更多的時候是描述

在一個陌生領域過早的使用繼承構建對象系統,往往很容易陷入重構甚至重寫的深淵。

相信我,絕大多數時候,繼承節約的敲代碼的時間遠不如重構需要的時間多。

那麼換個思路,我們不用繼承,用介面,個人習慣,介面都用形容詞命名,表示一個類的特徵。

依次實現「會跳動」、「有線連接」、「紡錘形」介面,然後你就實現了一個有線跳蛋類。

當你的老婆拿來一個無線跳彈,你只需要依次實現「會跳動」、「紡錘形」、「無線連接」介面,即可實現。雖然寫代碼的時間可能會比較長,但是無腦實現我想總要比絞盡腦汁理清對象樹輕鬆愉快。(此處舉例為Java8之前這種不能再介面中實現方法的情況)

那麼再進一步,C++中支持多繼承,往往多繼承被認為容易打亂思路,但如果利用多繼承來模擬Java中的介面呢?

基類都利用形容詞命名,而不是名詞,將多繼承的含義從「是什麼東西」變成「有什麼特點」,是否會兼具靈活和便利?

Java8中介面的default方法是否就是這個考慮?

似乎偏題了,其實以上的意思很簡單,多態未必是面向對象的必備要素,繼承更不是。

面向對象一直以來需要解決的僅僅是「對擁有同一個介面的一類對象,都可以不加處理的進行調用」,即調用者不需要考慮對象實現細節,僅需要考慮對象擁有哪些介面。

在Java/C++等語言中,這一點由編譯器強制保證;在Python等語言中,是利用扣工資等非技術手段逼迫開發人員自己保證。


有人這麼形容OP和OO的不同:用面向過程的方法寫出來的程序是一份蛋炒飯,而用面向對象寫出來的程序是一份蓋澆飯。所謂蓋澆飯,北京叫蓋飯,東北叫燴飯,廣東叫碟頭飯,就是在一碗白米飯上面澆上一份蓋菜,你喜歡什麼菜,你就澆上什麼菜。我覺得這個比喻還是比較貼切的。 蛋炒飯製作的細節,我不太清楚,因為我沒當過廚師,也不會做飯,但最後的一道工序肯定是把米飯和雞蛋混在一起炒勻。蓋澆飯呢,則是把米飯和蓋菜分別做好,你如果要一份紅燒肉蓋飯呢,就給你澆一份紅燒肉;如果要一份青椒土豆蓋澆飯,就給澆一份青椒土豆絲。 蛋炒飯的好處就是入味均勻,吃起來香。如果恰巧你不愛吃雞蛋,只愛吃青菜的話,那麼唯一的辦法就是全部倒掉,重新做一份青菜炒飯了。蓋澆飯就沒這麼多麻煩,你只需要把上面的蓋菜撥掉,更換一份蓋菜就可以了。蓋澆飯的缺點是入味不均,可能沒有蛋炒飯那麼香。 到底是蛋炒飯好還是蓋澆飯好呢?其實這類問題都很難回答,非要比個上下高低的話,就必須設定一個場景,否則只能說是各有所長。如果大家都不是美食家,沒那麼多講究,那麼從飯館角度來講的話,做蓋澆飯顯然比蛋炒飯更有優勢,他可以組合出來任意多的組合,而且不會浪費。 蓋澆飯的好處就是「菜」「飯」分離,從而提高了製作蓋澆飯的靈活性。飯不滿意就換飯,菜不滿意換菜。用軟體工程的專業術語就是「可維護性」比較好,「飯」 和「菜」的耦合度比較低。蛋炒飯將「蛋」「飯」攪和在一起,想換「蛋」「飯」中任何一種都很困難,耦合度很高,以至於「可維護性」比較差。軟體工程追求的目標之一就是可維護性,可維護性主要表現在3個方面:可理解性、可測試性和可修改性。面向對象的好處之一就是顯著的改善了軟體系統的可維護性。 面向過程(OP)和面向對象(OO)是不是就是指編碼的兩種方式呢?不是!你拿到了一個用戶需求,比如有人要找你編個軟體,你是不是需要經過需求分析,然後進行總體/詳細設計,最後編碼,才能最終寫出軟體,交付給用戶。這個過程是符合人類基本行為方式的:先想做什麼,再想如何去做,最後才是做事情。有的同學說:「我沒按照你說的步驟做啊,我是直接編碼的」。其實,你一定會經歷了這三個階段,只不過你潛意識裡沒有分得那麼清楚。對於拿到需求就編碼的人,可能編著編著,又得倒回去重新琢磨,還是免不了這些過程, 以OO為例,對應於軟體開發的過程,OO衍生出3個概念:OOA、OOD和OOP。採用面向對象進行分析的方式稱為OOA,採用面向對象進行設計的方式稱為OOD,採用面向對象進行編碼的方式稱為OOP。面向過程(OP)和面向對象(OO)本質的區別在於分析方式的不同,最終導致了編碼方式的不同。~~~非原創


所謂的「面向過程」或者是「面向對象」,僅僅只是編程思路的區別,它們解決問題的側重點不同,在開發方法上有差異。

拿printf("Hello world")來舉例說明:

面向過程的思路是關心printf()這個函數本身提供什麼樣的功能,也叫作功能模塊,比如除了列印"Hello world",還可以列印"Hello Kitty","Hello特特"等等;

而面向對象的思路,則關心"Hello world"這個字元串本身的屬性以及可以做哪些操作,也叫作對象的屬性和方法,比如"Hello world"可以列印,也可以計算它的長度,也可以數數它裡面包含了幾個"o"。

所以,只是編程思路的區別。但其實面向對象更加高級一點,因為它把功能模塊封裝起來了,變成了它的「方法」,而這些功能模塊的底層,都是面向過程的思路來實現的。

那麼,所謂的面向對象編程語言,也只不過是能更好的貫徹面向對象的編程思路,提供了「類」這麼一種專門用於對象操作的數據類型。然而,在C這種不是為面向對象而專門設計的語言中,我們依然可以貫徹面向對象的思想,這個我也舉一個例子,就是開源的sqlite,C語言庫,所有的介面都是面向對象風格。


面向對象:

public class Person
{
public string Name;

public bool MakeLove(Person spouse)
{
return this.Name != null spouse.Name != null this.Name.FirstOrDefault() != spouse.Name.FirstOrDefault();
}
}

面向過程:

public struct Person
{
public string Name;
}

public static class PersonHelper
{
public static bool MakeLove(Person boy, Person girl)
{
return boy.Name != null girl.Name != null boy.Name.FirstOrDefault() != girl.Name.FirstOrDefault();
}
}


用炒菜的例子來說下吧,假如我們要做一盤魚香肉絲。

面向過程:

  • 準備好豬肉,切成絲;準備冬筍,切成絲;準備好胡蘿蔔,切成絲;準備好黑木耳,切成絲。
  • 將鍋放在火上,開火,加入油,燒熱。
  • 將各種佐料放進去,炒。
  • 出鍋。

面向對象:

  • 創建一個廚師的類。

  • 為初始類添加炒魚香肉絲的方法。

  • 實例化廚師,調用炒魚香肉絲的方法。

由以上,我們可以看出,面向過程主要是強調過程,即炒菜的每個步驟。而面向對象呢,由前面的回答我們可以知道面向對象的三大原則是:封裝,繼承,多態。在上面舉的例子里,主要強調一個封裝,即對外隱藏做菜的具體實現方法,外部只需要調用做菜這個方法,得到菜就行了。那麼面向對象這樣干有什麼好處呢?面向對象更符合我們對這個世界的抽象。比如:在做菜這個例子里,在面向過程里,是自己一步一步把菜做出來,而面向對象可以進行角色的分離,抽象出一個廚師的類來,這樣更符合現實世界。減少思維的複雜性。

以上是面向過程和面向對象的一個區別,百度百科(面向對象_百度百科)里對這個的總結還不錯,可以一看。

另外,題主您的問題提得太寬泛了,如果能詳細說出來是哪一點不明白,可能會更容易回答,同時也會加強你對這個問題的理解。


我很憂傷,你們怎麼不能按照套路答題,面向對象的四個特點不是抽象、封裝、繼承、多態?直接一句話就完事了啊。

如果是三個特點,那就是封裝、繼承、多態。

看了下答案,絕大部分答案都是在講抽象和封裝,C裡面也有抽象,也有封裝啊,為什麼都說C是面向過程的。如果一個語言是面向對象的,那麼這個語言至少要支持抽象、封裝、繼承、多態,重要的話說三遍:

抽象、封裝、繼承、多態

抽象、封裝、繼承、多態

抽象、封裝、繼承、多態

一個初學者只要把這四個詞搜索一下搞懂了,就可以初步理解面向對象是什麼東西,當然還要搜索一下什麼是類,什麼是對象。按照套路來好么,不要講一堆似是而非的故事,打些自以為是的比方,反而不利於初學者的理解,人家理解上照成了偏差,誰負責?

C只支持一些簡單的抽象和封裝,甚至有點多態,比如運算符的操作數是支持一點多態的。但C++才是一個完整的面向對象語言,並且為了更好的支持面向對象,挖了一個又一個坑,然後後面這麼多年就是為當年的腦洞打開擦屁股了,以至於知乎上沒人敢說自己『精通C++』,當然,另一個梗是『PHP是...』

打住,打住,這又是另一個故事了。

我先逃為敬!


分享一個神級回答:

原帖:xi zhao:什麼是面向對象編程思想?

面向對象是相對於面向過程的,比如你要充話費,你會想,可以下個支付寶,然後綁定銀行卡,然後在淘寶上買卡,自己沖,這種種過程。但是對於你女朋友就不一樣了,她是面向「對象」的,她會想,誰會充話費呢?當然是你了,她就給你電話,然後你把之前的做了一遍,然後她收到到帳的簡訊,說了句,親愛的。這就是面向對象!女的思維大部分是面向「對象」的!她不關心處理的細節,只關心誰可以,和結果!


關鍵還是在於對程序的描述方式上

打個比方 假如你每天的工作就是分別和三個同事校對三個報表 那麼如果分別用面向過程和面向對象的方法描述你每天的工作的話就是

1.面向過程: 我的老闆每天都會給我,給我的同事小張,小紅和小明各一份財務報表,我每天的工作就是先和同事小張校對報表是否一致 然後再和同事小紅校對報表是否一致 最後再和小明校對報表是否一致

2.面向對象:我每天的工作就是看我手中的報表是否和其他三個同事手中的報表一致

前者的描述中 重點在於你工作的流程

後者的描述中 重點在於你工作遇到的實際的對象(你的同事某某手中的報表)

然後問題來了 如果有一天 小明辭職不幹了 小紅的位置被新來的小李頂替了 老闆給你們的工作也不再是校對財務報表而是變成了校隊庫存報表了 那麼你要怎樣修改你對你自己每天工作的描述呢?


我的想法:

1,本質:最終都要變成機器代碼,都是一堆二進位數,所有語言以及設計思想,對於機器沒有差異,所謂面向過程以及面向對象,都是人的思考方式而已。

2,執行過程,以前我搞單片機的時候(面向過程,C語言)就是去找main方法,一行一行地讀下去,調用什麼就是什麼,後來我去搞了安卓(面向對象,java),其實安卓程序也有一個入口,只是沒有那麼明顯,也是一行行讀下去,不過更多的並不是執行什麼,而是初始化了什麼,設置了些什麼東西。在調單片機程序的時候,一行行找找到出問題的地方,在做安卓那個時候,我去找他調用了那個類,那個方法,這個方法出了什麼問題。

3,封裝,模塊,其實面向過程也有模塊也有封裝,C語言裡面叫做文件,頭文件,以及對應的C文件,Java裡面不叫文件,人們叫類,C頭文件里有很多函數,Java的類裡面也有很多函數。但是相比較而言呢,Java封裝的更徹底,模塊劃分得更細緻。

4,數據結構,用C語言可以定義數據結構,Java裡面的類,我是感覺和C裡面數據結構非常的像,用C可以實現一個鏈表,java也能實現,在C裡面就是諸如:typedef struct..之類,在JAVA裡面,就是class ArrayList。面向對象就像是「進化」了後的數據結構。

5,使用,面向過程你想使用別人造的輪子,去找文件,找頭文件,頭文件里就有方法,面向對象你想使用那些輪子,你去找類,比如在C&,java里 String s;

總而言之,對於電腦,什麼思想都是一樣,都是一堆二進位數,而對於人,面向對象就像是對面向過程更加細化,功能模塊更加清晰,就像大公司裡面的分工,每個人都知道且只知道自己該做以及能做什麼,只管自己哪些工作,不論其他。基於面向對象思考問題,也讓問題更加細化,當程序出現了問題,只要追溯到指定的類就行了。

個人淺見,都是做了項目後的一些思考,不當之處,還請見諒


看到幾個比喻很有意思,來湊熱鬧。

有人說:面向過程是編年體;面向對象是紀傳體。

又有人說:用面向過程的方法寫出來的程序是一份蛋炒飯,而用面向對象寫出來的程序是一份蓋澆飯。

哎呀,你們會玩啊,我也來,我要用寫小說來舉例。

小說嘛,無非就是角色之間互動加上時空背景。

面向對象和面向過程就是兩條不同的寫作思路。。

面向對象是先設計角色模板,再來設計角色之間的互動,是一種組合的思路,互動中暗含了時間和過程。

面向過程是根據故事的時間軸來寫作,開端—發展—高潮—結局,是一條線性的時間軸,故事發展中也暗含了各個角色的變化。

說到各部分之間的關係,前者是網,後者是線。。。

大致如此。

兩者其實就是我們在解決問題的過程中,兩條不同的通用思路,前者核心是問題的時間順序,後者核心是問題的參與者和他們之間的互動。


封裝:把屬性值、紅藍條、攻擊、走位、放技能、清兵、遊走等行為都塞在一個英雄里。

繼承:攻擊+10 的裝備可以升級到攻擊+20,以後還可能升級到攻擊+30 並帶有吸血效果。不管升級成什麼,都攜帶著攻擊+10 這部分屬性。

多態:一個團隊需要一個輔助,我們只需要一個輔助英雄,並不關心來的是哪個輔助英雄,能加血就行。

具備這三種特性的編程思想,叫做面向對象。


假設有個機器人,這個機器人太蠢了,以至於只能接受兩個指令,分別是「原地左轉」和「前進一步」。

現在我們要命令這個機器人從下圖的左下角沿路線移動到A點,面朝上方(機器人初始面上):

那麼我們要下達的指令是:【左左左前左前左左左前左前左左左前左;】

完美的完成了任務,這就是面向過程。

但是你一定覺得不爽。

經過縝密的思考之後,我們終於發現了一個驚天秘密:三個左轉就是右轉啊!

但是機器人太蠢了不會右轉怎麼辦?

我們可以通過某些方法間接讓它右轉,比如這樣:

【右=左左左】

然後我們的指令就變成了:【右=左左左;右前左前右前左前右前左;】

看起來舒服多了,這就是面向對象。

需要注意的是,只是你看起來舒服而已,你的蠢貨機器人仍然只會左轉

接下來我們要讓機器人完成第二個任務,從下圖的左下角移動到A點

面向過程的方案是這樣的:

指令:【前前前前左左左前前前前前前左;】

雖然任務完成的很好,但是你在數前進次數時一定很痛苦,數錯了就是嚴重bug,你會被殺死祭天

而面向對象的設計思路是這樣的:

這樣就設計的好處就是,我們可以把任務二分解成兩個任務一

然後我們就可以拷貝代碼了:

指令:【右=左左左;右前左前右前左前右前左;右前左前右前左前右前左;】

這個方案是從任務一拷貝來的,所以你幾乎不需要怎麼思考就能知道這個方案一定沒問題

甚至,你都不需要關心任務一到底是怎麼完成的,比如這樣:

指令:【任務一;任務一;】

是不是清爽多了。

值得一提的是,雖然面向對象很清爽,但很明顯方案一運行的更快,在一些需要性能的場合里是很重要的,所以並面向過程並不是一無是處。

總結一下的話,犧牲一部分性能,讓程序員工作起來更簡單,就是面向對象

用最簡單的方法完成任務,就是面向過程


推薦閱讀:

C C++ Python哪個更適合新手?
為什麼編程語言中,循環計數器的默認變數都是「i」?
C 語言中字元串常量的好處在哪裡?
C 語言如何判斷等差數列?
在C語言中,math.h中定義的各種數學函數在電腦上具體是怎麼實現的?

TAG:程序員 | C編程語言 | 面向對象編程 | C# | 面向過程 |