在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    Java程序員不喜歡Golang的地方 – Gavin

    我愛(ài)Go。從我開(kāi)始使用這種語(yǔ)言的第一天起,我就迅速愛(ài)上了它。它提供了令人難以置信的簡(jiǎn)單性,同時(shí)保持了出色的類(lèi)型安全和快如閃電的編譯。它的執(zhí)行速度非??欤l(fā)性是一流的(這是一種輕描淡寫(xiě)的說(shuō)法),標(biāo)準(zhǔn)庫(kù)有大量的高級(jí)接口,可以用很少的依賴(lài)性來(lái)啟動(dòng)任何應(yīng)用程序,它可以直接編譯成可執(zhí)行文件,我可以繼續(xù)說(shuō)下去。盡管與其他C語(yǔ)言相比,Go的語(yǔ)法文字主義需要一些時(shí)間來(lái)適應(yīng),但在使用了一段時(shí)間后,感覺(jué)非常直觀。我的背景是Java,但對(duì)C、C++、JavaScript、TypeScript和Python都有豐富的經(jīng)驗(yàn)。Go是我學(xué)習(xí)的第一種語(yǔ)言,我希望在任何地方都能用它來(lái)做任何事情。雖然我討厭 “產(chǎn)品殺手 “這種陳詞濫調(diào)的概念,但作為一個(gè)曾經(jīng)是專(zhuān)業(yè)的Java開(kāi)發(fā)者的人,Go感覺(jué)就像是一個(gè)Java殺手。我認(rèn)為Java會(huì)消失嗎?可能不會(huì)。我認(rèn)為Go會(huì)在流行程度上超過(guò)Java嗎?不太可能。然而,對(duì)我個(gè)人來(lái)說(shuō),我無(wú)法想象在任何情況下(除了維護(hù)大到無(wú)法重寫(xiě)的傳統(tǒng)產(chǎn)品),我寧愿使用Java而不是Go。

    說(shuō)到這里,你可能會(huì)想,”這篇文章不是應(yīng)該講你不喜歡Go的地方嗎?” 這是一個(gè)完全公平的問(wèn)題,我正要回答這個(gè)問(wèn)題,但重要的是要了解我有多喜歡Go,才能理解我為什么要抱怨它。那么就不多說(shuō)了,到底有什么可批評(píng)的呢?

    庫(kù)函數(shù)會(huì)修改其參數(shù)我對(duì)Go的第一個(gè)抱怨是我馬上就注意到的。許多內(nèi)置的庫(kù)函數(shù)修改它們的參數(shù)而不是返回新的結(jié)果。修改函數(shù)參數(shù)模糊了輸入和輸出之間的界限,最終破壞了代碼表現(xiàn)力。一個(gè)函數(shù)的表現(xiàn)力是指它通過(guò)其簽名清楚地傳達(dá)意義和意圖的能力;意義傳達(dá)得越清楚,這個(gè)函數(shù)的表現(xiàn)力就越強(qiáng)。表達(dá)性是可維護(hù)代碼的最重要方面。顯然,有些時(shí)候性能比表現(xiàn)力更有利于程序,但從可維護(hù)性的角度來(lái)看,表現(xiàn)力應(yīng)該始終是優(yōu)先考慮的。那為什么Go會(huì)這樣做呢?通過(guò)修改參數(shù)而不是返回新數(shù)據(jù),Go編譯器可以更好地跟蹤特定變量的生命周期。Go是一種垃圾收集(GC)語(yǔ)言,所以任何時(shí)候函數(shù)返回一個(gè)指針或由指針支持的類(lèi)型(例如一個(gè)片斷),都會(huì)增加內(nèi)存需要在堆而不是棧上分配的可能性。堆分配需要垃圾收集,而垃圾收集會(huì)占用你程序中寶貴的CPU周期。

    避免堆分配–從而減少垃圾收集–絕對(duì)可以提高一個(gè)應(yīng)用程序的性能。這些優(yōu)化可能有利于渲染高FPS圖形的軟件,但對(duì)于大多數(shù)企業(yè)應(yīng)用和日常服務(wù)來(lái)說(shuō),很可能對(duì)終端用戶的好處幾乎是無(wú)法察覺(jué)的。一個(gè)合理的論點(diǎn)是,一種語(yǔ)言應(yīng)該盡量減少其自身庫(kù)的開(kāi)銷(xiāo),但是當(dāng)速度和易用性是相互競(jìng)爭(zhēng)的目標(biāo)時(shí),需要優(yōu)先考慮一個(gè)目標(biāo)。已經(jīng)有很多語(yǔ)言為高性能的使用場(chǎng)景提供了顯式內(nèi)存管理(C、C++、Rust等),那么Go真的應(yīng)該為了提供更少的GC周期而犧牲其最大的優(yōu)勢(shì)之一(易用性)嗎?

    作為一個(gè)開(kāi)發(fā)者,我希望至少能有一些更有表現(xiàn)力、更直觀的替代品,在功能上與優(yōu)化的API相當(dāng)。盡管有這樣的煩惱,Go遠(yuǎn)不是唯一犯了這樣錯(cuò)誤的語(yǔ)言,事實(shí)上,許多違規(guī)的Go函數(shù)都有幾乎相同的Java和C++對(duì)應(yīng)物。然而,僅僅因?yàn)橛衅渌Z(yǔ)言的先例,并不意味著Go應(yīng)該無(wú)可指責(zé),最終,這是我不喜歡這種語(yǔ)言的地方。為了挽回一些分?jǐn)?shù),可以說(shuō)要求用指針作為參數(shù)是Go語(yǔ)言請(qǐng)求修改的一種表達(dá)方式。對(duì)于這一點(diǎn),我將承認(rèn)幾點(diǎn),盡管我仍然沒(méi)有找到一個(gè)令人信服的方法來(lái)用slice這么做(而slice往往是這個(gè)模式用的最多的)。

    泛型Go 1.18引入了泛型,所以我有點(diǎn)被寵壞了,因?yàn)槲抑恍枰却龓讉€(gè)月就可以得到這個(gè)功能1,而許多資深的Go開(kāi)發(fā)者已經(jīng)等待了好幾年。Go的泛型實(shí)現(xiàn)感覺(jué)有點(diǎn)像TypeScript的,在大多數(shù)情況下,這是件好事。與TS一樣,開(kāi)發(fā)者可以很容易地約束泛型,使其符合幾種可能的已知類(lèi)型或接口。但與TS不同的是,Go不必處理JavaScript的包袱,特別是圍繞著未定義和null。說(shuō)白了,我喜歡泛型作為一種語(yǔ)言特性。如果使用得當(dāng),它們可以提高代碼的可重用性,從而提高一致性并降低錯(cuò)誤的風(fēng)險(xiǎn)。當(dāng)我說(shuō)我不喜歡Go中的泛型時(shí),我的意思有兩點(diǎn):第一,Go對(duì)泛型的實(shí)現(xiàn)還有很多需要改進(jìn)的地方;第二,長(zhǎng)期以來(lái)語(yǔ)言中缺乏泛型,導(dǎo)致許多丑陋的反模式埋藏在許多庫(kù)的表面,包括Go的標(biāo)準(zhǔn)庫(kù)。解讀第一點(diǎn),Go目前不支持方法或結(jié)構(gòu)域的泛型。對(duì)方法的不支持有點(diǎn)令人費(fèi)解,因?yàn)樵谝嫔w下,Go將方法視為以接收者為第一參數(shù)的函數(shù)。如果函數(shù)支持泛型,為什么方法不支持呢?Go對(duì)嵌入的支持降低了泛型結(jié)構(gòu)字段的關(guān)鍵性,因?yàn)楹芏喾盒偷挠猛径伎梢杂们度雭?lái)模仿:只要把 “非泛型 “字段放在一個(gè)單獨(dú)的結(jié)構(gòu)中,然后把同一個(gè)結(jié)構(gòu)嵌入幾次就好了。然而,嵌入并不是一個(gè)完美的替代品,因?yàn)椴僮骱头椒ㄐ枰槍?duì)外部結(jié)構(gòu)的每一種變化進(jìn)行重新實(shí)現(xiàn)。Go可以通過(guò)允許泛型結(jié)構(gòu)字段來(lái)將這一責(zé)任轉(zhuǎn)移給編譯器,而不是開(kāi)發(fā)人員,但現(xiàn)在我們還只能復(fù)制和粘貼。由于Go中目前缺少泛型的地方,許多數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)不得不使用反射、類(lèi)型檢查和鑄造等黑客的變通方法,以提供對(duì)不同類(lèi)型的廣泛支持。這讓我想到了第二個(gè)抱怨。Go作出了類(lèi)型安全的承諾,然后立即在其標(biāo)準(zhǔn)庫(kù)中通過(guò)使用偽通用的變通方法:interface{}來(lái)破壞它。Go的空接口用法不僅是一種反模式的縮影,而且類(lèi)型檢查和反射往往是較慢的操作(諷刺的是,這與我之前抱怨的表達(dá)能力對(duì)速度的權(quán)衡是不一致的)。最糟糕的是,第三方庫(kù)也大量采用了空接口的反模式,所以即使Go最終將其所有庫(kù)遷移到泛型,這種模式也可能在許多代碼庫(kù)中存在相當(dāng)長(zhǎng)一段時(shí)間。

    make()函數(shù)make()函數(shù)是Go的 “原始類(lèi)型 “初始化解決方案。大多數(shù)基元都有一個(gè)合理的零值,但在Go中,map、slices和channel都是受益于動(dòng)態(tài)初始化的基元類(lèi)型。使用map和slices的零值是完全可能的,有時(shí)甚至是合理的(例如JSON操作和避免nil返回),但對(duì)于大多數(shù)情況,make()是最好的選擇。我對(duì)make()有異議的地方是,它存在我已經(jīng)說(shuō)過(guò)的兩個(gè)問(wèn)題。首先,make()沒(méi)有表現(xiàn)力。它的完整簽名是func make(t Type, size …IntegerSize) Type,這讓我對(duì)如何正確使用它知之甚少。盡管它在技術(shù)上只是一個(gè)函數(shù),但在Go編譯器對(duì)它的特殊處理以及它對(duì)創(chuàng)建通道的必要性之間,make()就像for-loop一樣是Go的一個(gè)重要組成部分。采用這種思路可以部分地原諒它的簽名,但是提供NewMap()、NewSlice()和NewChan()函數(shù)也同樣容易,甚至更容易,這樣就不會(huì)產(chǎn)生歧義了。我不打算深入討論這些替代方案,因?yàn)槲蚁嘈艑?duì)于這些選擇為什么會(huì)有問(wèn)題,有很多強(qiáng)烈的意見(jiàn)。但我要深入探討的是,make()的錯(cuò)誤是多么容易發(fā)生(看到我做了什么了嗎)。m := make(map[int]int, 10) 創(chuàng)建一個(gè)空的地圖,分配足夠的空間來(lái)存儲(chǔ)10個(gè)條目;len(m) 返回0??吹絾?wèn)題所在了嗎?無(wú)論是在寫(xiě)代碼時(shí),還是在審查代碼時(shí),都很容易不小心忽略這個(gè)重要的區(qū)別。要獲得你所期望的切片行為,需要一個(gè)額外的參數(shù):s := make([]int, 0, 10)。在這種情況下,len(s)實(shí)際上會(huì)返回0。因此,Go并沒(méi)有為這些數(shù)據(jù)結(jié)構(gòu)提供更具表現(xiàn)力的、不同的初始化器,而是提供了一個(gè)具有更大模糊性的單一函數(shù),因此具有更大的誤用風(fēng)險(xiǎn)。在我對(duì)make()的看法上,我對(duì)它的第二個(gè)問(wèn)題是它的偽通用性。Go通常不允許函數(shù)重載,但make()得到了一個(gè)特殊的通行證來(lái)假裝重載。由于這個(gè)特殊的傳遞,make()的第一個(gè)參數(shù)可以是幾種類(lèi)型中的一種。它的返回類(lèi)型也是如此。對(duì)于一個(gè)十年來(lái)一直聲稱(chēng)不需要泛型的語(yǔ)言來(lái)說(shuō),Go不得不打破很多自己的規(guī)則,讓它最核心的一個(gè)函數(shù)在沒(méi)有泛型的情況下工作。對(duì)我來(lái)說(shuō),這讓我感覺(jué)很草率。

    扁平化的包結(jié)構(gòu)我來(lái)自Java的世界。Java應(yīng)用程序往往有很多很多的包。在這個(gè)世界上,父包對(duì)于一個(gè)類(lèi)的上下文來(lái)說(shuō)往往和類(lèi)的名字本身一樣重要,所以對(duì)于Go這個(gè) “Java殺手 “來(lái)說(shuō),擁有這樣一個(gè)扁平的包結(jié)構(gòu)是有點(diǎn)刺耳的。這并不是Go的獨(dú)特之處。許多面向腳本的語(yǔ)言,如Python,傾向于采用更多的廣度而不是深度。盡管這是一種相對(duì)普遍的做法,但我想讓Go成為Java的 “直接替代品 “的夢(mèng)想似乎已經(jīng)破滅了。扁平化的包結(jié)構(gòu)本身并沒(méi)有什么問(wèn)題。一層層的空目錄(或包含單個(gè)文件的目錄)在沒(méi)有明確設(shè)計(jì)的語(yǔ)言中很少提供價(jià)值–誠(chéng)然,這適用于大多數(shù)不是面向?qū)ο蟮恼Z(yǔ)言。然而,如果扁平語(yǔ)言聲稱(chēng)要解決與嵌套語(yǔ)言相同的問(wèn)題,那么扁平語(yǔ)言應(yīng)該提供語(yǔ)義上相等的機(jī)制來(lái)管理標(biāo)識(shí)符的可見(jiàn)性和范圍。

    Go通過(guò)大寫(xiě)字母導(dǎo)出標(biāo)識(shí)符的簡(jiǎn)單方法非常好。在我的團(tuán)隊(duì)的風(fēng)格公約會(huì)議上,我又少了一件要爭(zhēng)論的事情。玩笑歸玩笑,盡管我很喜歡Go開(kāi)發(fā)者的這一選擇,但包的語(yǔ)義和扁平結(jié)構(gòu)的慣例削弱了這一功能對(duì)應(yīng)用程序代碼的潛在價(jià)值。在庫(kù)代碼中,導(dǎo)出或不導(dǎo)出的簡(jiǎn)單概念對(duì)于定義公共API是完美的。對(duì)于大型應(yīng)用,尤其是網(wǎng)絡(luò)服務(wù)器,這通常是不夠的。網(wǎng)絡(luò)服務(wù)器必然會(huì)有一些從來(lái)沒(méi)有被其他代碼明確消費(fèi)的包(除了測(cè)試),而是被外部客戶和其他服務(wù)器通過(guò)HTTP等協(xié)議調(diào)用。這些包中的代碼將和其他代碼一樣從抽象中受益,但在扁平化的包結(jié)構(gòu)中,未導(dǎo)出的抽象將不可避免地被包中無(wú)權(quán)使用的其他區(qū)域看到。這導(dǎo)致了一個(gè)難題:我們應(yīng)該違反扁平化包結(jié)構(gòu)的慣例,犧牲可讀性和重用性來(lái)?yè)Q取更少的抽象性,還是干脆讓未導(dǎo)出的標(biāo)識(shí)符在它們不應(yīng)該出現(xiàn)的地方被訪問(wèn)?這個(gè)問(wèn)題的存在就是承認(rèn)Go有問(wèn)題。當(dāng)然,扁平結(jié)構(gòu)是慣例而不是法律,但慣例在Go的發(fā)展過(guò)程中具有巨大的影響力,它決定了許多新的功能,并將其納入語(yǔ)言。所以是的,這可能不是Go的一個(gè)明確特征,但它仍然是我不喜歡的東西,因?yàn)镚o社區(qū)把它作為一個(gè)最佳實(shí)踐而大力推動(dòng)。

    缺少lambda函數(shù)這個(gè)問(wèn)題絕對(duì)是吹毛求疵的,所以我就直奔主題了。Go并沒(méi)有λ函數(shù)的簡(jiǎn)寫(xiě)方式。我知道有人提議使用λ函數(shù),也有人爭(zhēng)論為什么不需要λ函數(shù),但盡管有這些考慮,事實(shí)是我喜歡速記λ,而Go沒(méi)有。Go的函數(shù)語(yǔ)法恰好是短而精的。此外,函數(shù)在Go中是類(lèi)型,可以分配給變量,這對(duì)我這個(gè)喜歡濫用Java 8中引入的 “方法引用 “的人來(lái)說(shuō)很熟悉。即使如此,當(dāng)我用Go寫(xiě)作時(shí),有時(shí)內(nèi)聯(lián)一個(gè)函數(shù)是解決一個(gè)問(wèn)題的最合適的方法,然而即使是單行的,所產(chǎn)生的代碼也往往是笨拙的,特別是當(dāng)需要返回語(yǔ)句時(shí)。我不認(rèn)為有人能說(shuō)服我說(shuō)func(x, y int) int { return x+y }比(x, y) => (x+y)更漂亮或更易讀。隨你怎么爭(zhēng)論強(qiáng)類(lèi)型或明確性,我還是會(huì)懷念速記的lambdas。

    總結(jié)對(duì)于那些還沒(méi)有嘗試過(guò)Go,但正在考慮使用它的人來(lái)說(shuō),不要讓這些勸阻你;它是一個(gè)神奇的工具,幾乎肯定會(huì)改善你的開(kāi)發(fā)生活。對(duì)于現(xiàn)有的Gophers,我希望你能同情我的抱怨,但仍然像我一樣喜歡這門(mén)語(yǔ)言。

    Java程序員不喜歡Golang的地方 – Gavin

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年6月28日 17:29
    下一篇 2022年6月28日 17:29

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息