我問占小狼到底什么是面向?qū)ο缶幊蹋克D(zhuǎn)頭就走。
你好,我是 yes。
面向?qū)ο缶幊滔氡卮蠹叶级炷茉?,但是寫了這么多代碼你對面向?qū)ο笥星逦恼J(rèn)識嗎?
來看看這幾個(gè)問題:
-
到底什么是面向?qū)ο缶幊蹋?/p>
-
和面向過程編程有什么區(qū)別?
-
什么又稱為面向?qū)ο笳Z言、面向過程語言?
-
用面向?qū)ο笳Z言寫的代碼就面向?qū)ο罅耍?/p>
-
面向?qū)ο缶幊陶娴木瓦@么好嗎?
-
復(fù)雜的業(yè)務(wù)用面向?qū)ο缶幊叹秃线m了嗎?
我還真沒具體地定義過到底什么是面向?qū)ο缶幊獭?/p>
所以假設(shè)有人問到底什么是面向?qū)ο缶幊??有什么好處?/p>
一時(shí)還真不知道怎么說,或者說成體系的解釋。
這篇文章我就談?wù)勎业睦斫猓苍囍茨懿荒苷f清啥叫面向?qū)ο缶幊獭?/p>
正文
從二進(jìn)制命令到匯編語言。
從匯編語言到面向過程語言再到面向?qū)ο笳Z言。
計(jì)算機(jī)語言的發(fā)展是為了便于人類的使用,使其更符合人類的思考方式。
計(jì)算機(jī)的思路就是取指執(zhí)行,一條直道走到底,它可不會管你什么抽象,不管什么業(yè)務(wù)建模,通通得給它變成一條條指令,排好順序讓它執(zhí)行。
而我們?nèi)祟惒灰粯樱覀兊乃季S在簡單場景來看是一條道,但在復(fù)雜場景就需要做各種分類,才能理清楚關(guān)系,處理好事務(wù)。
就像法庭,分為法官、書記員、法警、原告、被告、證人等角色。
這么多人分好類,按照法庭審理各司其職,一個(gè)案子才能高效、順利得審判。
再回到計(jì)算機(jī)語言來,匯編我就不說了,面向過程其實(shí)就是一條道的思路,因?yàn)槠鸪蹙褪前从?jì)算機(jī)的思路來編寫程序。
我就拿用咖啡機(jī)煮咖啡為例,按照面向過程的流程是:
-
執(zhí)行加咖啡豆方法 -
執(zhí)行加水方法 -
執(zhí)行煮咖啡方法 -
執(zhí)行喝咖啡方法
很簡單直觀的操作,你可能沒什么感覺,我再按面向?qū)ο笏枷雭矸治鱿逻@個(gè)流程。
在執(zhí)行煮咖啡操作前要抽象出:人和咖啡機(jī)(分類),然后開始執(zhí)行:
-
人.加咖啡豆 -
人.加水 -
咖啡機(jī).煮 -
人.喝咖啡
是不是有點(diǎn)感覺了?
面向過程,從名字可以得知重點(diǎn)是過程,而面向?qū)ο蟮闹攸c(diǎn)是對象。
從這個(gè)例子可以看出兩者的不同:面向過程是很直接的思維,一步步的執(zhí)行,一條道走到底。
而面向?qū)ο笫窍瘸橄?,把事物分類得到不同的類,劃分每個(gè)類的職責(zé),暴露出每個(gè)類所能執(zhí)行的動作,然后按邏輯執(zhí)行時(shí)調(diào)用每個(gè)類的方法即可,不關(guān)心內(nèi)部的邏輯。
從例子可以看出面向?qū)ο缶幊虉?zhí)行的步驟沒有變少,整體執(zhí)行流程還是一樣的,都是先加咖啡豆、加水、煮咖啡、喝,這個(gè)邏輯沒有變。
無非就是劃分了類,把每一步驟具體的實(shí)現(xiàn)封裝了起來,散布在不同的類中。
對我們程序員來說是最最直接的感受:變的其實(shí)就是代碼的分布,煮咖啡的代碼實(shí)現(xiàn)被封裝在咖啡機(jī)內(nèi)部,喝咖啡的代碼實(shí)現(xiàn)被封裝在人內(nèi)部,而不是在一個(gè)方法中寫出來。
代碼的分布確實(shí)是最直觀的,但是變得不僅只是分布,而是思想上的變化。
就是上面提到的計(jì)算機(jī)思維到人類思維的變化。
我認(rèn)為這個(gè)變化是因?yàn)檐浖陌l(fā)展,業(yè)務(wù)越來越復(fù)雜。
人們用面向過程語言編寫復(fù)雜的軟件時(shí),需要按照不同的功能把一些數(shù)據(jù)和函數(shù)放到不同的文件中,漸漸地人們就發(fā)現(xiàn)這不就是先分類嗎?
并且好像業(yè)務(wù)分析下來都能和現(xiàn)實(shí)世界的東西對應(yīng)上?
于是人們慢慢地總結(jié)、提煉就演變成了面向?qū)ο?,再根?jù)面向?qū)ο蟮奶匦蕴釤挸鲫P(guān)鍵點(diǎn):封裝、繼承和多態(tài)。
而這個(gè)面向?qū)ο笏枷刖皖愃莆覀內(nèi)祟惷鎸?fù)雜場景時(shí)候的分析思維:歸類、匯總。
所以面向?qū)ο缶幊叹统蔀榱爽F(xiàn)在主流的編程風(fēng)格,因?yàn)榉先祟惖乃伎挤绞健?/p>
面向過程編程和面向?qū)ο缶幊虖乃枷肷系淖兓牵簭挠?jì)算機(jī)思維轉(zhuǎn)變成了人類的思維來編寫編碼。
所以我們知道面向?qū)ο缶幊唐鋵?shí)是一種進(jìn)步,一種更貼近人類思考方式的編碼風(fēng)格,是源于人們用面向過程編程時(shí)的經(jīng)驗(yàn)總結(jié)。
至此我們知道了面向?qū)ο缶幊痰膩碓?/span>,相信知曉了來源能更好的理解面向?qū)ο蟆?/p>
面向?qū)ο缶幊?Object Oriented Programming,OOP)是一種編程范式或者說編程風(fēng)格。 學(xué)術(shù)一點(diǎn)講就是把類或?qū)ο笞鳛榛締卧獊斫M織代碼,并且運(yùn)用提煉出的:封裝、繼承和多態(tài)來作為代碼設(shè)計(jì)指導(dǎo)。 這其實(shí)就是面向?qū)ο缶幊獭?/p>
其實(shí)從上面煮咖啡的流程應(yīng)該能 get 到這個(gè)含義了。 OOP 說白了就是拿到需求開始分析,進(jìn)行抽象建立業(yè)務(wù)模型,每個(gè)模型建立對應(yīng)的類。 思考業(yè)務(wù)的交互,根據(jù)交互定義好接口并做好接口的控制訪問,將于此類相關(guān)的數(shù)據(jù)和動作都封裝起來。 抽象出父類,子類繼承父類來進(jìn)行代碼的復(fù)用和擴(kuò)展。 執(zhí)行功能時(shí)用父類來調(diào)用,在實(shí)際代碼運(yùn)行過程會進(jìn)行動態(tài)綁定,調(diào)用子類的實(shí)現(xiàn)達(dá)到多態(tài)的特性。 多態(tài),學(xué)術(shù)點(diǎn)講就是:運(yùn)行時(shí)用相同的代碼根據(jù)不同類型的實(shí)例呈現(xiàn)出不同行為的現(xiàn)象。 如果有新功能要實(shí)現(xiàn),只需要創(chuàng)建一個(gè)新子類,以前的執(zhí)行邏輯不需要發(fā)生變化,這就是「開閉原則」,對修改關(guān)閉,對擴(kuò)展開放”。 來簡單的看個(gè)代碼可能會有更直觀的感受,沒記錯的話大學(xué)時(shí)也是拿動物舉例。 狗是動物、鴨子是動物,所以有個(gè) Animal 類。 然后能發(fā)聲,所以有 voice 方法。 然后搞個(gè) Dog、Duck 繼承 Animal 實(shí)現(xiàn)各自的 voice。 然后到時(shí)候就可以實(shí)例化不同的對象來達(dá)到多態(tài)的效果。 多態(tài)帶來的好處,無非就是 Test 里面代碼不用動,你想要狗叫你就 new Dog 然后 set 進(jìn)去,如果要鴨子就 ?new Duck 然后 set 進(jìn)去。 如果加入了新動物那就建一個(gè)新動物類 set 進(jìn)去就行,符合開閉原則。 其實(shí)從上面煮咖啡和動物的這兩個(gè)例子應(yīng)該能感受出來區(qū)別。 最重要的是思想上的區(qū)別,上面也已經(jīng)提到了。 還有一點(diǎn)就是數(shù)據(jù)和動作。 面向過程編程這種編程風(fēng)格是以過程作為基本單元來組織代碼的,過程其實(shí)就是動作,對應(yīng)到代碼中來就是函數(shù),面向過程中函數(shù)和數(shù)據(jù)是分離的,數(shù)據(jù)其實(shí)就是成員變量。 而面向?qū)ο缶幊痰念愔袛?shù)據(jù)和動作是在一起的,這也是兩者的一個(gè)顯著的區(qū)別。 面向?qū)ο笳Z言其實(shí)就是有現(xiàn)成的語法機(jī)制來支持類、對象的語言,比如 Java。 當(dāng)然還要有支持繼承、多態(tài)的語法機(jī)制。 面向過程語言就反著理解,沒有現(xiàn)成的語法機(jī)制來支持類、對象等基本單元來組織代碼。 當(dāng)然不是你用了面向?qū)ο笳Z言寫出來的代碼就面向?qū)ο罅恕?/p>
你要通篇就一個(gè) class,一堆雜亂無章都往里面塞,不歸類、沒有封裝的意識,一條直到,這可不叫面向?qū)ο缶幊獭?/p>
當(dāng)然也不是用面向過程語言就寫不出面向?qū)ο蟮拇a,只是由于語法層面的不支持,寫起來沒那么方便,需要用一些手段,具體就不展開了。 所以語言只是為了更好的支持編程范式,重要的還是思想上的轉(zhuǎn)變。 結(jié)論先上:軟件設(shè)計(jì)沒有銀彈,沒有最好的,只有合適的。 前面也提到了面向?qū)ο蟾先祟惖乃伎挤绞?,這其實(shí)就是優(yōu)勢,能 hold 住復(fù)雜的需求。 復(fù)雜的需求關(guān)系都是錯綜復(fù)雜的,我們分類、抽象、封裝就能得到一個(gè)個(gè)規(guī)范化的模塊(類)。 大型項(xiàng)目都需要很多人協(xié)同合作,因?yàn)閯澐值那逦?,每個(gè)人只要實(shí)現(xiàn)自己負(fù)責(zé)的模塊。 然后根據(jù)模塊之間關(guān)系再組裝起來即可。 脈絡(luò)清晰也使得我們開發(fā)的時(shí)候思路也異常的清晰,提升開發(fā)的效率。 并且由于封裝的特性,類的內(nèi)部是高度內(nèi)聚的,會利用訪問控制權(quán)限暴露出有限的訪問,這使得類內(nèi)部的數(shù)據(jù)不會被隨意更改,提高代碼的維護(hù)性。 還有前面提到的繼承,提高代碼的復(fù)用性,由繼承實(shí)現(xiàn)的多態(tài)也符合開閉原則。 我還看過一個(gè)很形象的解釋(很久之前看過,忘了出處),說面向過程是蛋炒飯、面向?qū)ο笫巧w澆飯。 蛋炒飯混合在一起,蓋澆飯是分層的,如果不要蔥,蓋澆飯把上面的菜撥了直接換個(gè)沒蔥的菜,蛋炒飯就難搞了,得重新炒一份。 其實(shí)這個(gè)比喻體現(xiàn)的思想就是面向?qū)ο罂删S護(hù)性比較高,而且可以重用,更加靈活。 而面向過程就不易維護(hù),不易擴(kuò)展。 這個(gè)比喻沒錯,上面的說法也沒錯,但是我覺得需要加個(gè)前提:在合適的場景。 雖說我上面列了很多面向?qū)ο缶幊痰膬?yōu)點(diǎn),但是軟件設(shè)計(jì)沒有銀彈,沒有最好的,只有合適的。 當(dāng)你做一個(gè)很簡單的玩意,比如簡易計(jì)算器,你抽象來抽象去其實(shí)意義不大,直接按照面向過程的設(shè)計(jì)一條道走到底才是最合適的。 就像我們平日里面寫代碼,是否遇到個(gè)情況:為了一個(gè)功能需要新建一個(gè)類,然后類里面就一個(gè)方法。 因?yàn)榘凑彰嫦驅(qū)ο蟮乃季S,這個(gè)是需要抽象的。 然后為了復(fù)用還做了繼承、預(yù)留了一些接口等等,就想著以后擴(kuò)展。 可能過了很多年到這個(gè)項(xiàng)目撲街了,都沒擴(kuò)展上。 在項(xiàng)目里很多地方都做了這樣的鉤子,都白費(fèi),沒魚兒上鉤。 還不如當(dāng)時(shí)就直來直往的寫,繞來繞去的新同事進(jìn)來看的都一臉懵逼。 有些人說代碼就是得這樣寫,就是要為了之后的擴(kuò)展,設(shè)計(jì)模式上! 捫心自問一下,有多少之后用上了? 所以有很多大牛在那里罵: “面向?qū)ο缶幊淌且粋€(gè)極其糟糕的主意,只有硅谷里的人能干出這種事情?!?— Edsger Dijkstra(圖靈獎獲得者) “有時(shí),優(yōu)雅的實(shí)現(xiàn)只需要一個(gè)函數(shù)。不是一個(gè)方法。不是一個(gè)類,不是一個(gè)框架。只是一個(gè)方法?!?— John Carmack(id Software的創(chuàng)始人、第一人稱射擊游戲之父) “面向?qū)ο缶幊陶Z言的問題在于,它總是附帶著所有它需要的隱含環(huán)境。你想要一個(gè)香蕉,但得到的卻是一個(gè)大猩猩拿著香蕉,而其還有整個(gè)叢林?!?— Joe Armstrong(Erlang語言發(fā)明人) 還有挺多,我就不列出來了。 確實(shí)有時(shí)候?qū)懘a的時(shí)候能明顯感覺到有時(shí)候需要的只是一個(gè)函數(shù)。 所以對于那些:別問,問就是面向?qū)ο?/span>,還有一些大肆鼓吹設(shè)計(jì)模式的:別問,問就是設(shè)計(jì)模式 的人而言,我是不認(rèn)可的。 還是那句: 軟件設(shè)計(jì)沒有銀彈,沒有最好的,只有合適的。 一般的說法是面向?qū)ο筮m合復(fù)雜的場景,這句話其實(shí)也不全對。 當(dāng)時(shí)我在打 LOL 的時(shí)候就在感嘆,這技能的釋放,然后又因?yàn)榧恿?buf 可能有什么特別的計(jì)算,每一次版本變更好像改的東西挺多啊。 就像亞索出來的時(shí)候,這狂風(fēng)絕息斬對石頭人的大沒用,被蝎子拉了也沒用,這不是得做很多判斷啊。 每新出一個(gè)英雄,new 一個(gè)對象,其他英雄對象都得改啊,因?yàn)樾掠⑿坩槍Σ煌⑿劭赡苡胁灰粯拥膫πЧ?/p>
總之我覺得很復(fù)雜,每一次改動會涉及很多很多,所以腦子里面就有疑問這是怎么做的,面向?qū)ο蟮脑挼酶暮枚嘌健?/p>
前幾天我看到了知乎 invalid s 的回答,給我解了惑。 原來復(fù)雜的業(yè)務(wù)用面向?qū)ο缶幊踢€真不一定合適。 他舉的是 WOW 的例子,雖說我不知道 LOL 是不是這樣做的,但是這不重要。 他讓我知道在這個(gè)場景里面如果是以面向?qū)ο髞碓O(shè)計(jì),那面對如此繁多的職業(yè)、種族和技能,在頻繁地版本迭代下是招架不住的。 我截個(gè)圖,鏈接我放文末。 這種情況可以利用法術(shù)/技能數(shù)據(jù)庫化即表格化來解決。 所以從中可以看到不是面對復(fù)雜的場景就直接上面向?qū)ο蟮模?span style="font-weight: 600;color: rgb(60, 112, 198);">還是得具體情況具體分析,面向?qū)ο蟛皇侨f能的。那到底什么是面向?qū)ο缶幊蹋?/span>
????public?class?Animal?{
??????public?void?voice(){
??????????System.out.println("動物的叫聲");
??????}
????}????public?class?Dog?extends?Animal?{
??????public?void?voice(){
?????????System.out.println("汪汪汪~");
??????}
????}
???public??class?Duck?extends?Animal?{
??????public?void?voice(){
?????????System.out.println("gagaga~");
??????}
????}????public?class?Test{
??????private?Animal?animal;
??????public?void?setAnimal(Animal?animal)?{
????????this.animal?=?animal;
??????}
??????public?void?voice(){
??????????animal.voice();
??????}
????}和面向過程編程有什么區(qū)別?
什么又稱為面向?qū)ο笳Z言、面向過程語言
面向?qū)ο缶幊陶娴木瓦@么好嗎?
復(fù)雜的業(yè)務(wù)用面向?qū)ο缶幊叹秃线m了嗎?
最后
其實(shí)我還看到有人說面向?qū)ο蟮谋举|(zhì)是對真實(shí)世界的映射,這還真不一定。
我們平日寫代碼能很明顯地感受到有時(shí)候就是為了抽象和復(fù)用搞了一個(gè)類。
而且很多情況抽象出來的類和現(xiàn)實(shí)對應(yīng)不上,反正就是為了需求而造的一個(gè)類。
網(wǎng)上也看到很多言論,說啥 OOP 就是錯的、或者說 OOP 就是對的。
我覺得都很極端,還是那句軟件設(shè)計(jì)沒有銀彈,沒有最好的,只有合適的。
關(guān)于面向?qū)ο筮€想提一下。
在去年我寫 Fork/Join 的時(shí)候提到了分而治之。
面向?qū)ο笃鋵?shí)也有這味道。
拿古代舉例,皇帝其實(shí)不知道具體治理細(xì)節(jié),也不用管理具體細(xì)節(jié),一個(gè)國家這么大的龐然大物抽象了很多事務(wù),分成了很多類官員。
然后將每類官員需要做的事情封裝好,讓每類官員各司其職。
皇帝只需要統(tǒng)籌全局,根據(jù)每類官員的職責(zé)頒發(fā)不同的任務(wù)即可,不需要關(guān)心他具體是如何實(shí)施的。
皇帝只要說,讓各縣都推行啥啥啥,即可。
其實(shí)等于只要招呼縣令這個(gè)類去做事情,管你哪個(gè)縣,皇帝不需要關(guān)心。
然后每個(gè)縣令得到相同的命令,但是會有各自的治理方法,這其實(shí)就是多態(tài)。
這其實(shí)就是面向?qū)ο蟮乃枷搿?/p>
好了,說了這么多不知道能不能講清什么叫面向?qū)ο?,如果不清晰的話還望見諒,畢竟能力有限。
我再稍微的總結(jié)一下面向?qū)ο缶幊蹋?/p>
OOP 其實(shí)就是一種編程范式或者說風(fēng)格,是一種以類或?qū)ο鬄閱卧獊斫M織代碼的編碼方式,讓代碼高內(nèi)聚,低耦合。
OO 符合人類面對復(fù)雜事物時(shí)思考方式,抽象、建模、分類、歸類。
最后,歡迎加我好友進(jìn)行深入地交流,備注「進(jìn)群」,拉你進(jìn)交流&內(nèi)推群。
平日的面試題遇到難處,或者看某個(gè)知識點(diǎn)翻遍全網(wǎng)的資料還是感覺很模糊、不透徹,可以私聊我,給我留言。
遇到合適的我會整理寫出一篇文章,不會的我去請教別人也給整出來。
那種工作遇到很細(xì)節(jié)的場景的還是別了,這種問你上司比較合適:)
特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:
長按訂閱更多精彩▼
如有收獲,點(diǎn)個(gè)在看,誠摯感謝
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!