用c语言写代码_如何避免用动态语言的思维写Go代码
由于招聘市場(chǎng)上Go工程師的供給量不足,所以在招人的時(shí)候我們招了不少愿意轉(zhuǎn)型用Go語(yǔ)言進(jìn)行開(kāi)發(fā)的PHP工程師,不過(guò)雖說(shuō)換了個(gè)語(yǔ)言,在他們代碼的時(shí)候還是能發(fā)現(xiàn)很多PHP的影子。if語(yǔ)句后面非要帶括號(hào)這種問(wèn)題就不說(shuō)了,這屬于不懂事,gofmt就會(huì)強(qiáng)行把你掰過(guò)來(lái)。最大的問(wèn)題還是因?yàn)橐郧坝脩T了PHP的數(shù)組,到寫(xiě)Go代碼時(shí)還是不習(xí)慣先定義類型后使用這種習(xí)慣。還有就是以前寫(xiě)PHP的時(shí)候可能沒(méi)養(yǎng)成使用異常的習(xí)慣,在返回值里約定特殊值來(lái)代表錯(cuò)誤。所以后面我在團(tuán)隊(duì)內(nèi)部做過(guò)一次培訓(xùn),專門分享了怎么建立正確的Go編碼習(xí)慣,以下是節(jié)選了當(dāng)時(shí)演講稿的一部分。其實(shí)不是專門針對(duì)PHP程序員,可能寫(xiě)動(dòng)態(tài)語(yǔ)言的程序員在開(kāi)始用Go寫(xiě)代碼時(shí)都容易犯的一些錯(cuò)誤。
Go編程的注意事項(xiàng)及建議
接下來(lái)我們會(huì)說(shuō)幾個(gè)PHP程序員在剛開(kāi)始用Go寫(xiě)程序時(shí)幾個(gè)需要改變的編碼習(xí)慣和要注意的地方。
盡量使用結(jié)構(gòu)體切片代替字典
我們有的新同學(xué)特別愛(ài)使用Go里面的Map,有的時(shí)候還是切片里邊套Map,比如我看一開(kāi)始有的同學(xué)把一些配置信息放在map[string]string類型的Map里,多個(gè)的話再把Map放進(jìn)切片里,比如這樣。
var?configMap?=?[]map[string]string{?{??"stockNum":?"100",??"name":?????"芒果TV周卡",??"type":?????"virtual",????},}后面程序使用的時(shí)候再去用鍵去取值,這么做程序當(dāng)然能實(shí)現(xiàn),但你會(huì)發(fā)現(xiàn)Go里面因?yàn)槭菑?qiáng)類型,你在用上面字典里面的數(shù)值時(shí)還得對(duì)他們做類型轉(zhuǎn)換。很多同學(xué)馬上會(huì)說(shuō),那我把Map的類型換成map[string]interface{},我只能說(shuō)你試試,看你用的時(shí)候Go讓不讓你做類型斷言。
這其實(shí)是涉及一個(gè)思維的轉(zhuǎn)變,那么在像Go這樣的強(qiáng)類型語(yǔ)言里針對(duì)這種情況該怎么辦呢?這就需要讓我們養(yǎng)成先定義結(jié)構(gòu)體類型后使用的習(xí)慣了,比如像上面的情況我就可以先定義一個(gè)類型。
type?Product?struct?{?StockNum??int64?Name??????string?Type??????string}var?configs?=?[]*Product?{?{??StockNum:?100,??Name:?"芒果TV周卡",??Type:?"virtual",?},??......}這么做就能避免像上面那樣使用StockNum前還得把它轉(zhuǎn)成整型的問(wèn)題了,而且編輯器還能做類型提示,不需要你刻意記得Map里的鍵,還能避免你一時(shí)疏忽把鍵拼錯(cuò)導(dǎo)致BUG的尷尬。
除了上面說(shuō)的還有人喜歡在返回值里返回Map,這種寫(xiě)法除了會(huì)導(dǎo)致上面說(shuō)的那樣問(wèn)題,讓別人使用起來(lái)也特別不方便。比如我要用你的方法我還得進(jìn)去看看你的代碼里這個(gè)Map到底有哪些鍵。
所以我們寫(xiě)Go代碼時(shí),其實(shí)Map的使用率要比在PHP里使用數(shù)組低很多,很多時(shí)候都是用結(jié)構(gòu)體以及結(jié)構(gòu)體切片的,對(duì)于那種key為數(shù)據(jù)ID,值為數(shù)據(jù)Map的這種映射,也是改成Key為數(shù)據(jù)ID,值為數(shù)據(jù)自己定義的類型才對(duì)。比如下面這個(gè)Map類型的變量,它的Key是產(chǎn)品的ID,值的類型是我們上面定義的Product結(jié)構(gòu)體
var?productMap?=?map[int64]*Product?{?123:??{??StockNum:?100,??Name:?"芒果TV周卡",??Type:?"virtual",?},}針對(duì)這部分說(shuō)的這個(gè)問(wèn)題我覺(jué)得記住:**"根據(jù)數(shù)據(jù)先定類型再使用"**這個(gè)原則就行了。
說(shuō)完這個(gè)在代碼里出現(xiàn)率最高的問(wèn)題后,下面我們?cè)僬f(shuō)幾個(gè)寫(xiě)Go代碼時(shí)的要注意的細(xì)節(jié)。
零值陷阱
未進(jìn)行初始化的變量默認(rèn)值為其類型的零值,需要注意的是slice,map,chan和*T類型對(duì)應(yīng)的零值是nil。
這些類型的變量在未初始化前是無(wú)法在程序里直接使用的,有些情況下會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。
常見(jiàn)的兩種運(yùn)行時(shí)錯(cuò)誤是:
- panic: assignment to entry in nil map
- panic: invalid memory address or nil pointer dereference
第一個(gè)錯(cuò)誤是因?yàn)閷?duì)一個(gè)未初始化的map進(jìn)行賦值導(dǎo)致的,所以使用map類型的變量前要記得用make函數(shù)對(duì)變量進(jìn)行初始化,與map類似的切片在使用append函數(shù) 向nil slice追加新元素就可以,原因是append函數(shù)會(huì)生成新的切片,在底層為切片分配了底層數(shù)組。
第二個(gè)錯(cuò)誤是對(duì)nil指針進(jìn)行了解引用導(dǎo)致的,指針的零值nil與*T{}并不相等。所以指針類型的變量在使用前要注意使用new函數(shù)進(jìn)行初始化。
還有就是前端同學(xué)們非常不喜歡接口返回值的字段有數(shù)據(jù)的時(shí)候是個(gè)列表,沒(méi)數(shù)據(jù)的時(shí)候是Null,這也是切片未初始化導(dǎo)致的,如果數(shù)據(jù)庫(kù)里沒(méi)查到數(shù)據(jù),那么在代碼邏輯里就執(zhí)行不到給切片append數(shù)據(jù)的循環(huán)里,所以就會(huì)出現(xiàn)這個(gè)問(wèn)題。這是一個(gè)保持接口字段類型一致性的一個(gè)很重要的細(xì)節(jié)。
使用error返回函數(shù)錯(cuò)誤
在使用PHP時(shí),函數(shù)的錯(cuò)誤是通過(guò)拋出異常,甚至是通過(guò)返回0,false之類的值來(lái)表示函數(shù)遇到的錯(cuò)誤(這種,即使寫(xiě)PHP也不推薦這種做法)
比如好的寫(xiě)法,可這樣寫(xiě):
public?function?updateUserFavorites(User?$user,?$favoriteData){????try?{????????//?database?execution????......????}?catch?(QueryException?$queryException)?{????????throw?new?UserManageException(func_get_args(),?'Error?Message',?'501'?,?$queryException);????}????return?true;}但很多的人會(huì)這么寫(xiě):
public?function?updateUserFavorites(User?$user,?$favoriteData){????//?database?execution??if?($conn.AffectedRows?<=?0)?{????????return?false????}????return?true;}在Go語(yǔ)言里雖然沒(méi)有異常機(jī)制,但是可以讓函數(shù)返回error明確遇到的錯(cuò)誤。所以除非確定函數(shù)不需要返回error,多數(shù)情況下我們的函數(shù)都是需要返回error的,所以在定義函數(shù)時(shí)要明確,返回的數(shù)據(jù)和error的區(qū)別,兩種返回值的職責(zé)范圍不一樣。要通過(guò)函數(shù)返回的error是否為空,而不是返回?cái)?shù)據(jù)是0或者false之類的值判斷函數(shù)是否執(zhí)行成功。
謹(jǐn)慎使用map[string]interface{}做參數(shù)
寫(xiě)過(guò)PHP的同學(xué)都知道,PHP里的數(shù)組近乎萬(wàn)能,可以用來(lái)當(dāng)列表、字典,而且當(dāng)字典用時(shí)還能保證字典key的遍歷順序,這點(diǎn)是很多語(yǔ)言的字典類型辦不到的事情。
很多剛從PHP轉(zhuǎn)到用Go開(kāi)發(fā)的同學(xué)還是帶著在PHP里使用數(shù)組參數(shù)的習(xí)慣,那么在Go語(yǔ)言里,最像PHP數(shù)組的可能就是map[string]interface{}了。
這種還是典型的動(dòng)態(tài)語(yǔ)言編程的思維,在使用Go的時(shí)候,針對(duì)比較復(fù)雜的代表一類事物的參數(shù),我們也是應(yīng)該先定義結(jié)構(gòu)體,然后使用結(jié)構(gòu)體指針或者結(jié)構(gòu)體指針切片作為參數(shù)。盡量不使用map[string]interface{}這種類型的參數(shù),IDE也沒(méi)法幫助提示這些參數(shù)的內(nèi)部結(jié)構(gòu),這讓其他人使用這個(gè)代碼時(shí)就會(huì)很苦惱,還得先看看函數(shù)實(shí)現(xiàn)里具體用到了字典的哪些鍵。比如下面這兩個(gè)函數(shù)的對(duì)比:
type?UserInput?struct{??Name?????string??Age??????int32??Password?string}func?AuthenticateUser(input?*UserInput)?error?{????findUser(input.Name,?input.Password)????...}func?DummyAuthenticateUser(input?map[string]interface{})?error?{????findUser(input["name"],?input["password"])????...}一般在業(yè)務(wù)級(jí)別的程序開(kāi)發(fā)里,我們要傳遞存儲(chǔ)在數(shù)據(jù)表里的額外信息的時(shí)候才會(huì)使用到map[string]interface{}類型的參數(shù)。寫(xiě)表前把這部分?jǐn)?shù)據(jù)編碼成JSON格式再寫(xiě)入,當(dāng)然這個(gè)主要看使用場(chǎng)景,凡事沒(méi)有絕對(duì),這里只是強(qiáng)調(diào)一些在編碼習(xí)慣上的問(wèn)題。
總結(jié)
最近兩年在學(xué)習(xí)中我寫(xiě)了不少Go語(yǔ)言的文章,其中Web編程入門和Go并發(fā)編程這兩個(gè)系列我自認(rèn)為還是對(duì)新手很有幫助的。
來(lái)源:網(wǎng)管叨bi叨
作者:KevinYan11
總結(jié)
以上是生活随笔為你收集整理的用c语言写代码_如何避免用动态语言的思维写Go代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 平安好车主 买保险
- 下一篇: 新款 iPad Pro 会在什么时候发布