如何理解「面向對象編程的精髓在於將操作綁定在數據上」?
這句話準確嗎? 怎樣理解?
多年以後我發現,面向對象編程的精髓在於將操作綁定在數據上。在你還沒成為他們無歸屬組織的重要一員時,程序就還是由操作和數據構成。在典型的結構化程序設計之中,我們將精力集中在行為(動詞)上,然後弄清楚我們需要哪些數據(名詞)才能執行。換句話說,我們將數據綁定在行為上。但在面向對象程序設計中,數據,名詞,成為程序的中心,之後我們才去弄清楚要將哪些行為綁定在它們之上,藉此來解決因突發行為產生而可能造成的問題。
via http://www.iteye.com/news/23507
我曾經強調過很多次「面向對象是目前 GUI 不可缺少的技術;但其它領域要慎用,或者盡量不用面向對象」。所以我從來不能接受面向對象擁躉的這種莫名的感悟——「在典型的結構化程序設計之中,我們將精力集中在行為(動詞)上,然後弄清楚我們需要哪些數據(名詞)才能執行。」事實是,早在 UNIX 發展初期大師們就提出了 data structure over procedure 的文化。
Data dominates. If youve chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.
Data is more tractable than program logic. It follows that where you see a choice between complexity in data structures and complexity in code, choose the former. More: in evolving a design, you should actively seek ways to shift complexity from code to data.
以上摘自《Art of Unix Programming》,這絕非一本推崇 OOP 的書籍。所以我不知道面向對象所謂對「名詞」的重視算什麼新玩意。
實際上,如果讓 Linus 這樣的大師來說,他會說描述和在整個團隊中大力交流 data structure 的 invariable rules 更為重要。根據這些 invariable rules 加上大力的 code review,代碼質量就會提高。而 OOP 推崇的所謂數據和方法綁定的封裝,只是又一種期待用技術來取代平庸的程序員和糟糕的團隊交流(以及 code review)的幻想。
而在 GUI 領域,深層實現繼承才是 OOP 不可替代的原因。如 composition pattern,decorator pattern 都需要這種技術。OO最開始的目的是「模擬」現實中的結構;其次是降低GUI程序設計的門檻,但這更多是一個實現問題,而不是設計問題。
A Brief History of Object-Oriented Programming (http://web.eecs.utk.edu/~huangj/CS302S04/notes/oo-intro.html)SIMULA was the first object language. As its name suggests it was used to create simulations. Alan Kay, who was at the University of Utah at the time, liked what he saw in the SIMULA language. He had a vision of a personal computer that would provide graphics-oriented applications and he felt that a language like SIMULA would provide a good way for non-specialists to create these applications. He sold his vision to Xerox Parc and in the early 1970s, a team headed by Alan Kay at Xerox Parc created the first personal computer called the Dynabook. Smalltalk was the object-oriented language developed for programming the Dynabook. It was a simulation and graphics-oriented programming language. Smalltalk exists to this day although it is not widely used commercially.
@馮東 回答的OO在GUI編程的重要性是同意的,但是為什麼呢? 我覺得是所要管理的數據本身的特點決定的,如果你有一個演算法操作對象是一個數據類型為全integer的二位矩陣,在系統編程里,這種結構比較常見的,這種數據比較「深」或者說「經典」;但GUI程序所要處理的主要數據是像這樣的
Button
title: ...
color: ...
width: ...
height: ...
opacity: ...
這是「短」而「淺」的數據(或者說從數據結構的角度說是「鬆散」的),怎麼模擬這種多形態的數據呢?
OO只是個名字,class和object也只是正好被這麼叫而已,C裡面有struct,這種設計或者需要已經是顯而易見的了,如果沒有struct,C就很難模擬和處理一大類問題(非經典數據結構)。OO也是這個道理,高級一點的語言沒有struct,用Class來代替了。
函數綁定在數據上是精髓也不是精髓,是精髓是因為事實上這確實是很明顯的特徵,但要深究的話C的struct內同樣可以包含函數指針,可以做同樣的事情,區別只在於OO比C的實現看上去直觀一點而已(是否必要是另一個問題);實際上很多高級語言的底層實現不就是C的這種結構嗎?如果你的高級語言設計裡面去掉了指針,你是不是必須要一種替代結構能繼續把行為和數據綁定到一起呢(或者說把函數本身當成數據使用)?Object就是了。
如果一定要說一個OO做的事情,也絕對必要的話,就是「封裝」,「封裝」自然不是只有OO才有,而是一種general的思想,OO只不過是其中一種實現罷了。「繼承,多態」是實現使然,不是OO的必經之路,只不過大多數語言正好這麼做了而已。
完蛋了。OO的動作對應的是狀態,才不是數據。OO的精髓在我看來,在於你使用動作的時候碰不到狀態,至於數據,永遠都不應該跟動作一起寫到一個類里。只處理數據的東西,就是一個普通的函數,跟類毛關係都沒有。
類是什麼,類就是一個構造interface實例的構造函數。類本身最好甚至都不要直接引用,引用介面就好了。介面就是能力,不是單純的動作。譬如說【我會啪啪啪】就是一個介面,至於我啪啪啪有多少個做法,這是裡面的一個個的動作。那我啪啪啪的時候到底經歷了什麼心理【狀態】,你們是永遠也不知道的。
這就是封裝。
那麼多人總是捧著一個對OO錯誤的理解就開始噴,失望極了,對你們。
我認同這段話的說法,不認同「是繼承和多態」。多態是貼近底層的靜態語言中被強調的概念,因為變數被限制在某種類型上;而繼承的實質是很普通的原型查找。
要看這個問題不應該忽略了那些「沒有類」的面向對象語言。一個面向對象的語言可以沒有類,但不能沒有對操作的數據綁定。從 C++ 的 const T* this 到 Python 的 self,還有 ECMAScript 的上下文綁定,無非都是強調一件事,將數據綁定到特定的操作上,而且按照約定,只有這些操作(過程)才可以修改其所屬的數據結構。這個層面才是范型的層面,與函數式編程所要求的「純函數」形成鮮明對比。換一種說法,面向對象的實質是規定了一種使用函數副作用的方式。通過消息通訊,解耦各個模塊才是OO的精髓啊。
------------------------------2013年3月4日16:45:40更進一步地說,封裝、繼承和多態,都是當年simula時代的東西,當時只是有了對象的概念,而沒有現在OO的概念。現在人們常常談論起的OOP是阿蘭凱在PARC的時候發明的。
因為他有生物學背景,所以想像中各個模塊是各自以細胞的方式組織,好像放生物電一樣傳播信息來相互通訊並組織程序結構的。------------------------------2013年3月4日18:59:31
更進一步地說,現代OO主導的軟體工程在建模的時候,需要對領域模型進行問題空間的建模。
所謂的數據並不是數據,而是問題中的實體。所謂的操作並不是操作,而是一個實體能夠接受消息而採取的行為。然後就是,根據契約編程,讓對象根據消息而變化狀態。計算機科學的一條至關重要的原則是控制複雜性。一定要解耦,一定要把模塊很好地抽象出來。題主提到的這篇文章,重點不在於討論所謂面向對象的「精髓」,而在於討論在面向對象編程中,如何更好的進行封裝。它強調的是使用更好的名字來描述系統中的對象。Dont make objects that end with er. 如果問面向對象的精髓,個人覺得就一個詞,「對象」。其實就是你如何看待一個軟體系統。至於把一個軟體系統看成對象+消息還是數據+過程更加科學,那是另一個永恆的爭論。
根據我這十年來的開發經驗,基於/面向對象開發時,如何做好封裝才是基本。個人認為,就封裝而言,它和Data Structure Over Procedure的思路是一致的。也和題主所提到的那邊文章的中心思想是一致的,一個更加合適的名字能夠促進你做更好的封裝。
所以,我的觀點是,面向對象,首先要做好封裝。這是基本中的基本,這也是我個人經歷中看到的出問題最多的地方。如何組織數據結構,如何對業務建立一個好的數據模型,如何保證系統沒有冗餘,這是首先要做好的事情。可事實情況是(至少我經歷過的),大部分系統連封裝都沒有做紮實,更不要去談多態和繼承了。
--
-本來程序大體上是這樣的:是一堆數據上構建一堆操作(邏輯、演算法)。現在,
把數據和操作分發給各個對象,使其自製,互不干涉, (封裝)這樣出了問題比較容易定位。允許在已有對象上做增補和定製從而構建新的對象, (繼承)這樣可以鼓勵泛化的設計,控制對象代碼的複製。
既然某些對象滿足一種協議,那就該有一種消息通吃這些對象 (多態)這樣連對象之間消息代碼的複製也得到控制。這麼一來,我們只是成功的把程序變成:
一堆對象上構建一堆通信。實質上,抽象層次和操作粒度都往上推了一層(使得更適於某一類問題例如GUI),
但是這些對象們仍然可以雜亂任意的互相通信,不夠滿意,於是,
把這些對象分發給各個小組,每個組都有明確簡單的職責,組內的對象只和真正需要通信的最少的其他對象通信, (解耦)知道的越多責任越大,所以不該知道的堅決不知道。
記得cocoa中Model-View-Control分組,M和V是不直接往來的。所以,本質是一層層往上抽象,精髓是如何巧妙的抽象。 (設計模式)---人認識世界的基本思維方式是先看這個世界有什麼東西,再看這些東西在做什麼,再看它們之間是什麼關係。有什麼就是對象和類,它們在做什麼就是函數、方法(其實我一直搞不清類的成員函數和方法這兩個概念的區別),組成這個世界的基本元素就是基本數據類型和CPU指令、基本編程語句。
OO的精髓在於它在首先看這個世界有什麼東西然後再看它們在幹什麼這一點上與人的思維非常吻合。面向對象三大特性:封裝,繼承和多態;
用幾年下來,發現大家都在繼承,多態,然後是在此基礎上的各種模式,而「封裝」很少用到。因為有了封裝,可以做面向介面設計和編程,讓協作更容易,讓開發更關注介面輸出,讓程序更加健壯和可控。個人覺得:面向對象的最重要的特性,是「封裝」,或者是很多人稱為的「間接」。
LZ提到的「操作綁定在數據上」,任何一個方法(函數)都是可以做到的,但說到對象,應該首先是對象職責的劃分,其次才是對象對外的服務介面和內部數據。純粹個人多年編程下來的理解。這不是精髓,這是福利。雖然將數據的變化和相應的操作綁定是可以節約很多代碼,但用繼承之後會發現節約更多的代碼。
對於錯誤的理解完全沒法理解
樓主所引用的話看似莫名其妙,其實把這段話和某人引用的《Art of Unix Programming》那段話連起來看,就能看出端倪。
其實確實都是經驗之談,殊途同歸,只是不太好理解罷了。
之前我給一個新同事講如何設計良好的數據結構,來讓函數更簡單,思路更清晰。兩周後東西做完,慘不忍睹。
所以語言有些時候總是乏力的,上面這些總結並不能讓你明白你還沒領會到的東西,只是讓你在領會到這些東西之後,得到強化和共鳴。面向對象方法旨在提高代碼的可維護性和可重用性,也就是當需求變化了,能夠少量修改就可以適應新的需求的穩定可靠的代碼。(換言之,就是在類似但不同的場景可以通過盡量少的代碼修改使用已有的代碼)
代碼的可維護性一部分來自可讀性。當問題空間和解決空間使用相同的概念,代碼就易於理解。面向對象方法的對象,屬性,操作,繼承,就來自我們對問題空間(真實世界)的認識,概念,共性,特性等。
可維護性還在於修改一部分代碼時,對其他部分的代碼的影響小。面向對象方法通過將數據和操作放置在對象/類的概念下,將屬性的操作封裝為方法,提供繼承和多態,減少代碼的重複。面向介面的編程,將實現與介面隔離。將責任在類之間分布,實現高內聚,低耦合。
面向對象方法將穩定的和易變的隔離開來。例如,通過介面封裝易變的邏輯,例如模板方法,在抽象類中定義一組抽象的方法,由子類實現,調用這些方法的邏輯可能是穩定的,而這些方法的實現邏輯可能是易變的。通過依賴介面而不依賴具體實現,再藉助依賴注入,可不修改一行代碼,就可以改變應用系統的邏輯。是繼承和多態詳見@狼大人 的答案
那些年搞不懂的高深術語——依賴倒置?控制反轉?依賴注入?面向介面編程
那些年,空氣中彷彿還能聞到漢唐盛世的餘韻,因此你決不允許自己的臉上有油光,時刻保持活力。然而,你一定曾為這些「高深術語」感到過困擾。也許時至今日,你仍對它們一知半解。不過就在今天,這一切都將徹底改變!我將帶領你以一種全新的高清視角進入奇妙的編程世界,領略涵泳在這些「高深術語」中的活潑潑的地氣,以及翩躚於青萍之末的雲水禪心。

·內聚
內聚,通俗的來講,就是自己的東西自己保管,自己的事情自己做。
經典理論告訴我們,程序的兩大要素:一個是數據(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系統等任何需要分散式通信的軟體系統中——這種良好的擴展性都與「依賴注入」的使用密不可分!
·面向介面編程談到最後,「面向介面編程」已經是呼之欲出。無論是依賴倒置、控制反轉、還是依賴注入,都已經蘊含著「面向介面編程」的思想。面向介面,就意味著面向抽象。作為哲學範疇而言,規定性少稱為抽象,規定性多稱為具體。而介面,就是程序中的一種典型的「抽象」的形式。面向抽象,就意味著面向事物的本質規定性,擺脫感性雜多的牽絆,從而把握住「必然」——而這本身就意味著自由,因為自由就是對必然的認識。
也許以上的這段論述太過「哲學」,但是「一本之理」與「萬殊之理」本身就「體用不二」——總結來看,依賴倒置、控制反轉、依賴注入都圍繞著「解耦和」的問題,而同時自始至終又都是「面向介面編程」的方法——因此,「面向介面編程」天生就是「解耦和」的好辦法。由此也印證了從「抽象」到「自由」的這一段範疇的辯證衍化。
「面向對象」與「面向介面」並非兩種不同的方法學,「面向介面」其實是「面向對象」的內在要求,是其一部分內涵的集中表述。我們對於理想軟體的期待常被概括為「高內聚,低耦合」,這也是整個現代軟體開發方法學所追求的目標。面向對象方法學作為現代軟體開發方法學的代表,本身就蘊含著「高內聚,低耦合」的思想精髓,從這個意義上來說,「面向對象」這個表述更加側重於「高內聚」,「面向介面」的表述則更加側重於「低耦合」——不過是同一事物的不同側面罷了。
除此之外,我們也能從「面向介面編程」的思想中得到「世俗」的啟迪——《論語》裡面講,不患無位,患所以立;不患人之不己知,患其不能也——就是教導我們要面向「我有沒有的本事?」、「我有沒有能力?」這樣的介面,而不是面向「我有沒有搞到位子?」、「別人了不了解我?」這樣的具體。依我看,這是莫大的教誨!
封裝不是面向對象獨有的。寫得好的c程序能明顯看到清晰的模塊邊界和隱藏數據的做法。面向對象最大的特徵在於用抽象來簡化系統結構。但抽象的不好就是災難。
直接的回答是不準確。在面向對象之前曾經有ADT(抽象數據類型)一說,其精華正是【將操作綁定在數據上】,之後才出現面向對象的概念乃至面向對象的語言,如果把這一點描述成定義為OO的精髓,那麼此後幾十年等價於在做枝節工作,這是很扯的。具體來講,封裝並不只意味著把操作與數據的綁定,而是要描述某一概念的邊界,想想純虛類或者說介面類。各種框架大多依賴於介面與實現的分離。最後也說兩句跑題的話,面向對象與面向過程各自有著自己的適用邊界,把握這種邊界是非常困難的事情,但一條基本原則是:過程式代碼(使用數據結構的代碼)便於在不改動既有數據結構的前提下添加新函數,面向對象代碼便於在不改動既有函數的前提下添加新類。我在自己的書里使了很大勁試圖把這個說明白,可以看了的人都說看不太明白。
嘛,樓上各種回答都好專業,我屬於業餘學習,只說說個人的理解。
操作好比動詞,變數好比名詞,但是把他們寫在紙上,都是需要用文字這種東西,文字就是一種數據。比如說有個集合類叫人,人可以干各種事情,於是在這個類下寫了各種操作方法,比如吃早飯(人){}
吃中飯(人){}等等,這些方法老要寫人這個參數,太蛋疼了,於是就把這些個玩意放到一個類里,用這些方法時候默認涉及這個參數:人。class 人{吃早飯(){}吃中飯(){}}然後就把這種偷懶的好辦法叫做對象編程。其實這個公共變數不限於一個名詞,也可以是動詞,比如汪星人要吃飯,喵星人也要吃飯,於是我們也可以做一個吃飯類
class 吃飯{喵星人(){}汪星人(){}}於是不管是吃早飯,吃中飯,吃晚飯,都是吃飯,都可以用這個類裡面的操作方法,上面的操作吃飯,變成了下面的一個對象,公共參數,一個數據。上面的人這個數據,到了下面成了喵星人,汪星人兩個操作方法。我們可以看到,操作和數據,在記錄和編程中,並沒有啥區別。事實上,把人,吃飯這兩個看作一個變數組,以上兩個類直接就可以歸於一個函數:運算 (人 , 吃飯){}人和吃飯都成了一個參數,被記作了數據。事實上,在學習一定的函數式編程如LISP和理解lambda運算之後,再考慮一下數學裡群論的把運算和運算的對象放在一個集合里討論的思想,在深入理解面向對象里的數學含義後,就能想明白,命名空間,類這些集合層級能成立,最終也是建立在集合論公理(包括選擇公理)上的。OO的價值在於對象以及對象之間的關係。以及如果利用這樣的工具來描述真實世界(建模)。其他都是浮雲。
面向對象的精髓在於適合抽象業務,至於怎麼抽象各種方法各種設計模式,操作綁不綁數據只是視情況而定
面向對象的類感覺上是一種受限的宏,而繼承和多態是實現宏的方式。目的就是重用代碼。
面向對象就是把數據和操作綁在一起——但是然後,你要把 它們 分開,這個是種設計模式。面向對象提供了一種類似命名空間,方便模塊化的一種方式。推薦閱讀:
※類(class)能不能自己繼承自己?
※新手求教python 面向對象編程的一個問題?
※為什麼有的人連OO、FP等基本的語言概念都搞不清楚,卻整天吹噓OO/FP的好處?
※我有很多隻筆(鉛筆、圓珠筆),但是我聲明一個類「筆」作為各種筆的父類有什麼意義呢?
