摘要:因?yàn)槭且环N長(zhǎng)程的關(guān)聯(lián),基類的修改對(duì)繼承者的影響難以估計(jì)和維護(hù),具有特性,因此從耦合度的角度說(shuō),它大大提高了組件的耦合度,高耦合度不是罪過(guò),但它應(yīng)對(duì)變化的能力變差了。
JavaScript開發(fā)者的繁殖速度和它的語(yǔ)言特性一樣迅猛,這是好事,但是也把JS搞得比任何一種其他語(yǔ)言都更像流行樂(lè),充滿教派和玄學(xué)。但編程不是玄學(xué),是科學(xué)和工程。這篇文章就用來(lái)闡述和探討JavaScript中的一個(gè)比較關(guān)鍵的概念,雖然在實(shí)踐上并不如在理論上那么意義重大。
Prototype Inheritance是JavaScript里的一個(gè)標(biāo)志性特性。實(shí)際上它叫做Inheritance是有一些問(wèn)題的,JS沒(méi)有type系統(tǒng),instanceof也只是一個(gè)沿著原型鏈查找constructor的語(yǔ)法糖,一個(gè)對(duì)象是誰(shuí)構(gòu)造出來(lái)的并不說(shuō)明任何問(wèn)題,因?yàn)樗膶?duì)象沒(méi)有結(jié)構(gòu)上的穩(wěn)定性承諾,只能靠程序員自覺(jué)。
Inheritance是OO近30年的工程實(shí)踐里留下來(lái)的重要特性,但是它不是一個(gè)好的特性。當(dāng)然好與不好是相對(duì)的,在絕大多數(shù)情況下它都不是太大的問(wèn)題,尤其是工程進(jìn)入尾聲,開發(fā)者對(duì)問(wèn)題和模型有充分認(rèn)識(shí)的時(shí)候,Class Hierarchy可以是很合理的設(shè)計(jì)。
那么,在更General的層面上去問(wèn)Inheritance設(shè)計(jì)解決的是什么問(wèn)題呢??jī)蓚€(gè)字,reuse。
在Java里,reuse有兩個(gè)語(yǔ)法關(guān)鍵字,一個(gè)是extends,即inheritance,另一個(gè)是implements,實(shí)現(xiàn)interface。
那么為什么把implements也當(dāng)成reuse呢?因?yàn)槿魏文K總有兩個(gè)方面,使用者和提供者,implements實(shí)現(xiàn)了一個(gè)interface,所以等價(jià)于重用了使用者代碼。
當(dāng)然你說(shuō)extends也達(dá)到了同樣的目的呀?而且我還重用了父類的狀態(tài)和行為呢?是的,凡是兩面,有得有失;這正是它倒霉的地方。
它倒霉的具體情況被稱為fragile base class問(wèn)題,wiki上有詞條,不贅述。
因?yàn)閑xtends/inheritance是一種長(zhǎng)程的關(guān)聯(lián),基類的修改對(duì)繼承者的影響難以估計(jì)和維護(hù),具有ripple effect特性,因此從耦合度的角度說(shuō),它大大提高了組件的耦合度,高耦合度不是罪過(guò),但它應(yīng)對(duì)變化的能力變差了。
好了,說(shuō)了這么多我們說(shuō)到了問(wèn)題的本原。
在Self語(yǔ)言中,也是最早試圖解決這個(gè)問(wèn)題的語(yǔ)言設(shè)計(jì)者們,給出了Prototypal Inheritance設(shè)計(jì),
它的設(shè)計(jì)初衷有兩個(gè):
抹平Class和Object的差異,讓修改基類變得容易;
如果你修改基類,復(fù)制一個(gè)基類對(duì)象然后修改,新繼承者從新的基類對(duì)象開始繼承。其他繼承者不受影響。
你覺(jué)得這個(gè)差別很重要嗎?其實(shí)在實(shí)踐上沒(méi)有想象的那么重要。
JavaScript在設(shè)計(jì)上還有點(diǎn)不同,它的原型對(duì)象是共享而非復(fù)制的,結(jié)果是只適合把方法裝載到原型上去,偶爾有一些同類對(duì)象相同的只讀context也可以這樣做,其他每對(duì)象私有態(tài)還得通過(guò)調(diào)用父類構(gòu)造函數(shù)做出來(lái),即ES6里的super關(guān)鍵字,如果是ES5,得手動(dòng)把this bind到父類構(gòu)造函數(shù)上調(diào)用。這個(gè)特性就語(yǔ)言而言是重要特性,但是和我們討論的問(wèn)題沒(méi)太大關(guān)系。
更重要的問(wèn)題出在設(shè)計(jì)上而不是語(yǔ)言層面。
James Gosling在一次研討會(huì)上回答問(wèn)題時(shí),有人問(wèn)了他這樣一個(gè)問(wèn)題,如果重新設(shè)計(jì)Java語(yǔ)言,他會(huì)有什么重要的取舍。Gosling的回答可能有點(diǎn)兒令你吃驚:他說(shuō)如果可以重新來(lái)過(guò),他不會(huì)賦予Java語(yǔ)言繼承特性,只用implements。
呃?!
其實(shí)是可以理解的。
繼承設(shè)計(jì)看起來(lái)在代碼重用上很方便,但是它的fragile base class問(wèn)題,讓它無(wú)法應(yīng)對(duì)軟件系統(tǒng)的scale問(wèn)題。這一點(diǎn)不用論證,在整個(gè)軟件工業(yè)上,繼承這種whitebox reuse不能scale是一個(gè)定論,單一程序用繼承書寫代碼不是問(wèn)題,但是任何有點(diǎn)規(guī)模的系統(tǒng)都是靠interface,protocol,或者所謂的component-based工程方法來(lái)搭建的,也就是在更高粒度的設(shè)計(jì)層面只有基于Interface的blackbox reuse。
在任何語(yǔ)言中,都能用blackbox reuse構(gòu)建復(fù)雜系統(tǒng)。在JavaScript中,龐大的npm包系統(tǒng)實(shí)現(xiàn)的最終應(yīng)用,也是blackbox reuse。
JavaScript本身是否functional是洗剪吹們喜歡探討的,嚴(yán)肅的工程師不該干這個(gè)事兒;但是整個(gè)JavaScript的Community的共識(shí)是基于blackbox reuse構(gòu)建系統(tǒng),這是好事。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
那么是否該使用extends呢?
很多時(shí)候沒(méi)什么必要性。既然Gosling都說(shuō)不該去通過(guò)inheritance-based hierarchy去組合復(fù)雜行為(犧牲低耦合度),誰(shuí)還比他更有資格對(duì)這個(gè)問(wèn)題發(fā)言呢?
通??梢园褞讉€(gè)邏輯單元糖葫蘆一樣串起來(lái)實(shí)現(xiàn)一個(gè)從外部看來(lái)功能特別Powerful的一個(gè)對(duì)象時(shí),也很容易把每個(gè)獨(dú)立單元用decorator,facade之類的pattern串起來(lái),可能會(huì)多寫點(diǎn)兒代碼,但不會(huì)很多,JavaScript作為無(wú)類型動(dòng)態(tài)語(yǔ)言在書寫pattern時(shí)具有顯著的簡(jiǎn)潔優(yōu)勢(shì)。(在C++/Java里是相反的,只寫inheritance顯著比寫pattern簡(jiǎn)潔;但是問(wèn)題就是問(wèn)題你無(wú)法回避,如果必須要松開耦合,還得回到pattern上定義,這也是為什么這些pattern被發(fā)明出來(lái)的原因。)
當(dāng)然用extends方便的時(shí)候也沒(méi)必要去抵觸它,比如node里的event emitter,stream等等,該用就用唄,只要不去試圖構(gòu)造framework一樣的hierarchy即可。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
如果有人跟你說(shuō)JavaScript不是OO語(yǔ)言,請(qǐng)一腳把它踹溝里去,JS里除了用Object Literal寫出來(lái)的ex nihilo對(duì)象之外,(邏輯上)所有對(duì)象都是用構(gòu)造函數(shù)構(gòu)造出來(lái)的,這甚至包括全局的Object, Array,F(xiàn)unction等等,一切皆對(duì)象是JS的最高設(shè)計(jì)思想。你怎么能說(shuō)它不是OO的?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/86529.html
摘要:首先,需要來(lái)理清一些基礎(chǔ)的計(jì)算機(jī)編程概念編程哲學(xué)與設(shè)計(jì)模式計(jì)算機(jī)編程理念源自于對(duì)現(xiàn)實(shí)抽象的哲學(xué)思考,面向?qū)ο缶幊淌瞧湟环N思維方式,與它并駕齊驅(qū)的是另外兩種思路過(guò)程式和函數(shù)式編程。 JavaScript 中的原型機(jī)制一直以來(lái)都被眾多開發(fā)者(包括本人)低估甚至忽視了,這是因?yàn)榻^大多數(shù)人沒(méi)有想要深刻理解這個(gè)機(jī)制的內(nèi)涵,以及越來(lái)越多的開發(fā)者缺乏計(jì)算機(jī)編程相關(guān)的基礎(chǔ)知識(shí)。對(duì)于這樣的開發(fā)者來(lái)說(shuō) J...
摘要:所支持的面向?qū)ο缶幊贪ㄔ屠^承。發(fā)明于年的就是首批支持函數(shù)式編程的語(yǔ)言之一,而演算則可以說(shuō)是孕育了這門語(yǔ)言。即使在今天,這個(gè)家族的編程語(yǔ)言應(yīng)用范圍依然很廣。 1. 能說(shuō)出來(lái)兩種對(duì)于 JavaScript 工程師很重要的編程范式么? JavaScript 是一門多范式(multi-paradigm)的編程語(yǔ)言,它既支持命令式(imperative)/面向過(guò)程(procedural)編程...
摘要:所支持的面向?qū)ο缶幊贪ㄔ屠^承。發(fā)明于年的就是首批支持函數(shù)式編程的語(yǔ)言之一,而演算則可以說(shuō)是孕育了這門語(yǔ)言。即使在今天,這個(gè)家族的編程語(yǔ)言應(yīng)用范圍依然很廣。 1. 能說(shuō)出來(lái)兩種對(duì)于 JavaScript 工程師很重要的編程范式么? JavaScript 是一門多范式(multi-paradigm)的編程語(yǔ)言,它既支持命令式(imperative)/面向過(guò)程(procedural)編程...
摘要:所支持的面向?qū)ο缶幊贪ㄔ屠^承。發(fā)明于年的就是首批支持函數(shù)式編程的語(yǔ)言之一,而演算則可以說(shuō)是孕育了這門語(yǔ)言。即使在今天,這個(gè)家族的編程語(yǔ)言應(yīng)用范圍依然很廣。 1. 能說(shuō)出來(lái)兩種對(duì)于 JavaScript 工程師很重要的編程范式么? JavaScript 是一門多范式(multi-paradigm)的編程語(yǔ)言,它既支持命令式(imperative)/面向過(guò)程(procedural)編程...
閱讀 2822·2023-04-25 14:21
閱讀 1246·2021-11-23 09:51
閱讀 4174·2021-09-22 15:43
閱讀 674·2019-08-30 15:55
閱讀 1643·2019-08-29 11:28
閱讀 2506·2019-08-26 11:44
閱讀 1741·2019-08-23 18:15
閱讀 2939·2019-08-23 16:42