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