偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

編程新手入門踩過的25個(gè)“坑”,你犯過其中哪些錯(cuò)誤?

企業(yè)動(dòng)態(tài)
高級(jí)的編程是邏輯思維的流露,會(huì)編程只代表你懂了這門語言的語法,但是會(huì)寫清晰簡潔易懂可迭代的代碼才是程序員該追求的境界。

[[225267]]

大數(shù)據(jù)文摘作品

編譯:傅一洋、汪小七、張南星、GAO Ning、夏雅薇

高級(jí)的編程是邏輯思維的流露,會(huì)編程只代表你懂了這門語言的語法,但是會(huì)寫清晰簡潔易懂可迭代的代碼才是程序員該追求的境界。編程入門已經(jīng)不容易,但是如果能夠在早期樹立一些正確的“代碼觀”,或許可以讓你的編程之路升級(jí)得更快。作者苦口婆心地給出了25條建議,句句真言。

首先我要聲明的是:如果你是編程新手,本文并不是要讓你對(duì)自己犯的錯(cuò)誤感到愧疚,而是要你對(duì)這些錯(cuò)誤有更好的認(rèn)知,并避免在未來再犯。

當(dāng)然,這些錯(cuò)誤我也經(jīng)歷過,但是從每個(gè)錯(cuò)誤中都學(xué)到了一些新東西。現(xiàn)在,我已經(jīng)養(yǎng)成了一些好的編程習(xí)慣,我相信你也可以!

下面是這些常見的錯(cuò)誤,順序不分先后。

1. 寫代碼前缺少規(guī)劃

一般來說,創(chuàng)作一篇高質(zhì)量的文章不易,因?yàn)樗枰磸?fù)推敲研究,而高質(zhì)量的代碼也不例外。

編寫高質(zhì)量代碼是這樣一個(gè)流程:思考、調(diào)研、規(guī)劃、編寫、驗(yàn)證、修改。(貌似沒辦法編成一個(gè)好記的順口溜)

按照這樣的思路走,你會(huì)逐漸形成良好的編程習(xí)慣。

新手最大的錯(cuò)誤之一就是太急于寫代碼,而缺乏足夠的規(guī)劃和研究。雖然對(duì)于編寫小程序而言是沒多大問題的,但對(duì)于大項(xiàng)目的開發(fā),這樣做是很不利的。

為了防止代碼寫完之后發(fā)現(xiàn)重大問題,寫之前的深思熟慮是必不可少的。代碼只是你想法的流露。

生氣的時(shí)候,在開口說話前先數(shù)到十。如果非常生氣,就數(shù)到一百。

——托馬斯·杰斐遜

我把它改成針對(duì)寫代碼的版本:

審查代碼時(shí),重構(gòu)每一行之前,先數(shù)到十。如果代碼還沒有測(cè)試,就數(shù)到一百。

——Samer Buna

編程的過程主要是研讀之前的代碼,思考還需要修改什么,如何適應(yīng)當(dāng)前系統(tǒng),并規(guī)劃盡量小的改動(dòng)量。而實(shí)際編寫代碼的過程只占整個(gè)過程時(shí)間花費(fèi)的10%。

不要總認(rèn)為編程就是寫代碼。編程是基于邏輯的創(chuàng)造,慢工出細(xì)活。

2. 寫代碼之前規(guī)劃過度

雖說寫代碼前充分規(guī)劃是好,但凡事都有個(gè)度,還沒開始做,就思考太多,也是不可取的。

不要期望世界上存在完美的規(guī)劃,至少編程的世界中是不存在。好的規(guī)劃可以作為起點(diǎn),但實(shí)際情況是,規(guī)劃是會(huì)隨后續(xù)進(jìn)行而改變的,規(guī)劃的好處只是能讓程序結(jié)構(gòu)條理更清晰,而規(guī)劃太多只會(huì)浪費(fèi)時(shí)間。

瀑布式開發(fā)是一種系統(tǒng)線性規(guī)劃的開發(fā)方法,它嚴(yán)格遵循預(yù)先計(jì)劃的需求、分析、設(shè)計(jì)、編碼、測(cè)試的步驟順序進(jìn)行,步驟成果作為進(jìn)度的衡量標(biāo)準(zhǔn)。在這種方法中,規(guī)劃是重中之重。如果只是編寫小程序,也完全可以采用這種方法,但要對(duì)于大的項(xiàng)目,這種方法完全不可取。任何復(fù)雜的事情都需要根據(jù)實(shí)際情況隨機(jī)應(yīng)變。

編程是一個(gè)隨時(shí)需要根據(jù)實(shí)際情況作出改變的工作。你后續(xù)可能會(huì)因?yàn)橐恍┰蛞砑踊騽h除的某些功能,但這些情況瀑布計(jì)劃中可能你永遠(yuǎn)也想不到。所以,你需要敏捷的開發(fā)模式。

但是,每一步之前是要有所規(guī)劃的,只不過規(guī)劃的過少或過多都會(huì)影響代碼的質(zhì)量,代碼的質(zhì)量非常重要。

3. 低估代碼質(zhì)量的重要性

如果你無法兼顧代碼的多項(xiàng)質(zhì)量指標(biāo),至少要保證它的可讀性。凌亂的代碼就相當(dāng)于廢品,而且不可回收。

永遠(yuǎn)不要低估代碼質(zhì)量的重要性。你要將代碼看作溝通的一種方式,作為程序員,你的任務(wù)是交代清楚目前任務(wù)是如何實(shí)施的。

我最喜歡一句編程俚語是:

寫代碼的時(shí)候可以這樣想,維護(hù)你代碼的家伙是一個(gè)知道你住在哪里的暴力精神病患者。

——John Woods

很形象是不是?

即便是一些細(xì)節(jié)。例如,你的代碼可能會(huì)因?yàn)榕虐鎲栴}或大小寫不一致而不被認(rèn)可。

  1. tHIS is 
  2.   WAY MORE important 
  3. than 
  4.          you think 

還需要注意的是避免語句過長。任何超過80個(gè)字符的文本都是難以閱讀的。你可能想在同一行放置長條件以便看到完整的if語句,這是不可取的,一行永遠(yuǎn)不要超過80個(gè)字符。

這種小問題可以通過linting工具或格式化工具輕松解決。比如在JavaScript中兩個(gè)完美結(jié)合的優(yōu)秀工具:ESLint和Prettier。多用它們,讓工作更輕松。

還有一些與代碼質(zhì)量相關(guān)的錯(cuò)誤:

  • 任何超過10行的函數(shù)都太長了。
  • 一定不要出現(xiàn)雙重否定句。
  • 使用簡短的,通用的或基于類型的變量命名。盡量保證變量命名能清晰地表述變量。計(jì)算機(jī)科學(xué)領(lǐng)域只有兩件難事:緩存失效和變量命名。
  • 缺乏描述地插入一些字符串和數(shù)字。如果要使用固定的字符串或數(shù)值,應(yīng)該將其定義為常量,并命名。
  • “對(duì)于簡單的問題,擔(dān)心花費(fèi)時(shí)間而草率地處理”。不要在眾多問題中進(jìn)行跳躍式選擇,按部就班地來。
  • 認(rèn)為代碼越長越好。其實(shí),大多數(shù)情況下,代碼越短越好。只有在追求可讀性的情況下可適當(dāng)詳細(xì)些。比如,不要為了縮短代碼而使用很長的單行表達(dá)式或嵌套表達(dá)式,但也不要增加冗余的代碼。最好的是,刪去所有不必要的代碼。
  • 過多使用條件語句。大部分你認(rèn)為需要條件語句的情況都可以不通過

它來解決。因此,考慮盡可能多的備選方案,根據(jù)可讀性進(jìn)行挑選。除非你知道如何測(cè)試代碼性能,否則,不要試圖優(yōu)化。還有就是:避免Yoda條件或條件嵌套。

4. 選擇1號(hào)方案

當(dāng)我剛開始編程時(shí),一旦遇到問題,我會(huì)立刻尋找解決方案并重新運(yùn)行我的程序。而不是先考慮我的頭號(hào)方案復(fù)雜性和潛在的失敗原因。

雖然1號(hào)方案極具誘惑性,但在研究了所有解決方案后,通常能發(fā)現(xiàn)更好的。如果無法想出多種方案,說明你對(duì)問題了解不夠。

作為專業(yè)程序員,你的工作不是找到辦法,而是找到最簡捷的辦法。“簡捷”的意思是方案必須正確,可執(zhí)行,且足夠簡單,易讀,又便于理解和維護(hù)。

軟件設(shè)計(jì)有兩種方法。一種是設(shè)計(jì)的足夠簡單,沒有瑕疵,另一種是設(shè)計(jì)的足夠復(fù)雜,沒人看得出明顯瑕疵。

——C.A.R.霍爾

5. 吊死在一棵樹上

這是我常犯的錯(cuò)誤,即便確定了我的頭號(hào)方案并不是最簡單的解決方案,仍然不放手。這可能與我的性格有關(guān)。大多數(shù)情況下這是一種很好的心態(tài),但不適用于編程。事實(shí)上,正確的編程心態(tài)是,將早期失敗和經(jīng)常性失敗看成一種常態(tài)。

當(dāng)你開始懷疑某個(gè)方案的時(shí)候,你應(yīng)該考慮放下它并重新思考,不管你之前在它這里投入了多少精力。學(xué)會(huì)利用像GIT這樣的源代碼管理工具,它可以幫助你實(shí)現(xiàn)代碼分支,嘗試多種方案。

不要認(rèn)為你付出了精力的代碼就是必須采用的。錯(cuò)誤的代碼要摒棄。

6. 閉門造車

很多次,在解決問題需要查閱資料時(shí),我卻直接嘗試解決問題,浪費(fèi)了很多時(shí)間。

除非你正在使用的是某種尖端技術(shù),否則,遇到問題時(shí),谷歌一下吧,因?yàn)橐欢〞?huì)有人也遇到了同樣的問題,并找到了解決方法,這樣,能節(jié)省很多時(shí)間。

有時(shí)候谷歌之后,你會(huì)發(fā)現(xiàn)你所認(rèn)為的問題并不是問題,你需要做的不是修復(fù)而是接受。不要認(rèn)為你了解一切,Google會(huì)讓你大吃一驚的。

不過,要謹(jǐn)慎地使用谷歌。新手會(huì)犯的另一個(gè)錯(cuò)誤是,在不理解代碼的情況下,原樣照搬。盡管這可能成功解決了你的問題,但還是不要使用自己不完全了解的代碼。

如果想成為一名創(chuàng)造性的程序員,就永遠(yuǎn)不要認(rèn)為,自己對(duì)在做的事情了如指掌。

作為一個(gè)有創(chuàng)造力的人,最危險(xiǎn)的想法是認(rèn)為自己知道自己在做什么。

——布雷特·維克多

7. 不使用封裝

這一點(diǎn)不只是針對(duì)使用面向?qū)ο笳Z言的例子,封裝總是有用的,如果不使用封裝,會(huì)給系統(tǒng)的維護(hù)帶來很大的困難。

在應(yīng)用程序中,每個(gè)功能要與用來處理它的對(duì)象一一對(duì)應(yīng)。在構(gòu)建對(duì)象時(shí),除了保留被其他對(duì)象調(diào)用時(shí)必須傳遞的參數(shù),其他內(nèi)容都應(yīng)該封裝起來。

這不是出于保密,而是為減少應(yīng)用程序不同部分之間的依賴。堅(jiān)持這個(gè)原則,可以使你在對(duì)類,對(duì)象和函數(shù)的內(nèi)部進(jìn)行更改時(shí),更加的安全,無需擔(dān)心大規(guī)模的毀壞代碼。

對(duì)每一個(gè)邏輯概念單元或者塊都應(yīng)該構(gòu)建對(duì)應(yīng)的類。通過類能夠勾畫出程序的藍(lán)圖。這里的類可以是一個(gè)實(shí)際對(duì)象或一個(gè)方法對(duì)象,你也可以將它稱作模塊或包。

在每個(gè)類中,其包含的每套任務(wù)要有對(duì)應(yīng)的方法,方法只針對(duì)這一任務(wù)的執(zhí)行,且能成功的完成。相似的類可共同使用一種方法。

作為新手,我無法本能地為每一個(gè)概念單元?jiǎng)?chuàng)建一個(gè)新類,而且經(jīng)常無法確定哪些單元是獨(dú)立的。因此,如果你看到一套代碼中到處充斥著“Util”類,這套代碼一定是新手編寫的?;蛘?,你做了個(gè)簡單的修改,發(fā)現(xiàn)很多地方也要進(jìn)行相應(yīng)地修改,那么,這也是新手寫的。

在類中添加方法或在方法中添加更多功能前,兼顧自己的直覺,花時(shí)間仔細(xì)思考。不要認(rèn)為過后有機(jī)會(huì)重構(gòu)而馬虎跳過,要在第一次就做對(duì)。

總而言之,希望你的代碼能具有高內(nèi)聚性和低耦合性,這是一個(gè)特定術(shù)語。意思就是將相關(guān)的代碼放在一起(在一個(gè)類中),減少不同類之間的依賴。

8. 試圖規(guī)劃未知

在目前項(xiàng)目還正在編寫的時(shí)候,總是去想其他的解決方案,這是忌諱的。所有的謎團(tuán)都會(huì)隨著代碼的一行行編寫而逐一解開。如果,對(duì)于測(cè)試邊緣案例進(jìn)行假設(shè),是件好事,但如果總想要滿足潛在需求,是不可取的。

你要明確你的假設(shè)屬于哪一類,避免編寫目前并不需要的代碼,也不要空想什么計(jì)劃。

僅憑空想,就認(rèn)為未來會(huì)需要某種功能,因而嘗試編寫代碼,是不可取的。

根據(jù)目前的項(xiàng)目,始終尋求最少的代碼量。當(dāng)然,邊緣情況是要考慮的,但不要過早落實(shí)到代碼中。

為了增長而增長是癌細(xì)胞的意識(shí)形態(tài)。

——Edward Abbey

9. 錯(cuò)誤使用數(shù)據(jù)結(jié)構(gòu)

在準(zhǔn)備面試的時(shí)候,新手往往太過于關(guān)注算法。掌握好的算法并在需要時(shí)使用它們固然不錯(cuò),但記住,這與你的所謂“編程天賦資質(zhì)”無關(guān)。

然而,掌握你所用語言中各種數(shù)據(jù)結(jié)構(gòu)的優(yōu)缺點(diǎn),對(duì)你成為一名優(yōu)秀的開發(fā)者大有裨益。

一旦你的代碼中使用了錯(cuò)誤的數(shù)據(jù)結(jié)構(gòu),那明擺著,你就是個(gè)新手。

盡管本文并不是要教你數(shù)據(jù)結(jié)構(gòu),但我還是要提幾個(gè)錯(cuò)誤示例:

(1) 使用list(數(shù)組)來替代map(對(duì)象)

最常見的數(shù)據(jù)結(jié)構(gòu)錯(cuò)誤是,在管理記錄表時(shí),使用了list而非map。其實(shí),要管理記錄表,是應(yīng)該使用map的。

例如,在JavaScript中,最常見的列表結(jié)構(gòu)是數(shù)組,最常見的map結(jié)構(gòu)是對(duì)象(最新JavaScript版本中也包含圖結(jié)構(gòu))。

因此,用list來表示map結(jié)構(gòu)的數(shù)據(jù)是不可取的。雖然這種說法只是針對(duì)于大型數(shù)據(jù)集,但我認(rèn)為,任何情況下都應(yīng)如此,幾乎沒有什么情況,list能比map更好了,而且,這些極端情況在新版本的語言中也逐漸消失了。所以,只使用map就好。

這一點(diǎn)很重要。主要是由于訪問map中的元素會(huì)比訪問list中的元素快得多,訪問元素又是常有的過程。

在以前,list結(jié)構(gòu)是很重要的,因?yàn)樗鼙WC元素的順序,但現(xiàn)在,map結(jié)構(gòu)同樣能實(shí)現(xiàn)這個(gè)功能。

(2) 不使用棧

在編寫任何需要遞歸的代碼時(shí),總是去使用遞歸函數(shù)。但是,這樣的遞歸代碼難以優(yōu)化,特別在單線程環(huán)境下。

而且,優(yōu)化遞歸代碼還取決于遞歸函數(shù)返回的內(nèi)容。比如,優(yōu)化兩個(gè)或多個(gè)返回的遞歸函數(shù),就要比優(yōu)化單個(gè)返回值的遞歸函數(shù)困難得多。

新手常常忽略了使用棧來替代遞歸函數(shù)的做法。其實(shí),你可以運(yùn)用棧,將遞歸函數(shù)的調(diào)用變?yōu)閴簵_^程,而回溯變?yōu)閺棗_^程。

10. 把目前的代碼變得更糟

想象一下,給你這樣一間凌亂的房間:

[[225268]]

然后,要求你在房間里再增加一個(gè)物件。既然已經(jīng)一團(tuán)糟了,你可能會(huì)想,把它放在任何地方都可以吧。因此,很快就能完成任務(wù)。

但是,在編寫代碼時(shí),這樣做只會(huì)讓代碼越來越糟糕!你要做的是,保證代碼隨著開發(fā)的進(jìn)行,變得越來越清晰。

所以,對(duì)于那間凌亂的房間,正確的做法是:做必要的清理,以便能將新增的物品放置在正確的位置。比如,你要在衣柜中添置一件衣服,那就需要先清理好地面,留出一條通向衣柜的路,這是必要的一步。

以下是一些錯(cuò)誤的做法,通常會(huì)使代碼變得更糟糕(只舉了一部分例子):

  • 復(fù)制代碼。如果你貪圖省事而復(fù)制代碼,那么,只會(huì)讓代碼更加混亂。就好比,要在混亂的房間中,添加一把新椅子,而不是調(diào)整現(xiàn)有椅子的高度。因此,頭腦中始終要有抽象的概念,并盡可能地去使用它。
  • 不使用配置文件。如果你的某個(gè)值在不同時(shí)間、不同環(huán)境下是不一樣的,則該值應(yīng)寫入配置文件中?;蛘?,你需要在代碼中的多個(gè)位置使用某值,也應(yīng)將它寫入配置文件。這樣的話,當(dāng)你引入一個(gè)新的值時(shí),只需要問自己:該值是否已經(jīng)存在于配置文件?答案很可能是肯定的。
  • 使用不必要的條件語句或臨時(shí)變量。每個(gè)if語句都包含邏輯上的分支,需要進(jìn)行雙重測(cè)試。因此,在不影響可讀性的情況下,盡量避免使用條件語句。與之相關(guān)的一個(gè)錯(cuò)誤就是,使用分支邏輯來擴(kuò)展函數(shù),而不去引入新函數(shù)。每當(dāng)你認(rèn)為你需要一個(gè)if語句或一個(gè)新的函數(shù)變量時(shí),先問問自己:是否在將代碼往正確的方向推進(jìn)?有沒有站在更高的層面去思考問題?

關(guān)于不必要的if語句的問題,參考一段代碼:

  1. function isOdd(number) { 
  2.   if (number % 2 === 1) { 
  3.     return true; 
  4.   } else { 
  5.     return false; 
  6.   } 

上面的isOdd函數(shù)是存在一些問題的,你能看出最明顯問題嗎?

那就是,它使用了一個(gè)不必要的if語句。以下為其等效的代碼:

  1. function isOdd(number) { 
  2.   return (number % 2 === 1); 
  3. }; 

11. 注釋泛濫

我已經(jīng)學(xué)會(huì)了,盡量不去寫注釋。因?yàn)榇蠖鄶?shù)的注釋可以通過對(duì)變量更好的命名來代替。

例如以下代碼:

  1. // This function sums only odd numbers in an array 
  2. const sum = (val) => { 
  3.   return val.reduce((a, b) => { 
  4.     if (b % 2 === 1) { // If the current number is even 
  5.       a+=b;            // Add current number to accumulator 
  6.     } 
  7.     return a;          // The accumulator 
  8.   }, 0); 
  9. }; 

其實(shí),也可以寫成這樣沒有注釋的,效果相同:

  1. const sumOddValues = (array) => { 
  2.   return array.reduce((accumulator, currentNumber) => { 
  3.     if (isOdd(currentNumber)) { 
  4.       return accumulator + currentNumber; 
  5.     } 
  6.     return accumulator; 
  7.   }, 0); 
  8. }; 

所以,每次寫注釋前,先思考一下:能否通過改善參數(shù)的命名來避免寫注釋呢?

但有一些情況下,是必須寫注釋的。比如,當(dāng)你用需要注釋來表述代碼的目的,而不是代碼在做什么時(shí)。

如果你實(shí)在想寫注釋的話,那就不要描述那些過于明顯的問題。以下是一些無用注釋的例子,它們只會(huì)干擾代碼的閱讀:

  1. // create a variable and initialize it to 0 
  2. let sum = 0
  3. // Loop over array 
  4. array.forEach( 
  5.   // For each number in the array 
  6.   (number) => { 
  7.     // Add the current number to the sum variable 
  8.     sum += number; 
  9.   } 
  10. ); 

所以,不要成為這樣的程序員,也不要接受這樣的代碼。如果必須處理這些注釋的話,那就刪掉好了。要是碰巧你雇傭的程序員總是寫出這樣的代碼的話,快點(diǎn)解雇他們。

12. 不寫測(cè)試

我認(rèn)同這一點(diǎn):如果你自認(rèn)為是專家,且有信心在不測(cè)試的情況下編寫代碼,那么在我看來,你就是個(gè)新手。

如果不編寫測(cè)試代碼,而用手動(dòng)方式測(cè)試程序,比如你正在構(gòu)建一個(gè)Web應(yīng)用,在每寫幾行代碼后就刷新并與應(yīng)用程序交互的話,我也這樣做過,這沒什么問題。

但是,手動(dòng)測(cè)試代碼,是為了更明確如何在之后進(jìn)行自動(dòng)測(cè)試。如果成功測(cè)試了與應(yīng)用的交互,那就應(yīng)該返回到代碼編輯頁,編寫自動(dòng)測(cè)試代碼,以便下次向項(xiàng)目添加更多代碼時(shí),自動(dòng)執(zhí)行完全相同的測(cè)試。

畢竟,作為人類,每次更改代碼后,難免會(huì)有忘記去重新測(cè)試曾經(jīng)成功過的代碼,所以,還是把它交給計(jì)算機(jī)完成吧!

如果可以,就在編寫代碼之前,先猜測(cè)或設(shè)計(jì)測(cè)試的過程。測(cè)試驅(qū)動(dòng)開發(fā)(TDD)這種方法不僅僅是流行,它還能使你對(duì)功能的看法發(fā)生積極的變化,以及為它們提供更好的設(shè)計(jì)方案。

TDD并不適合每個(gè)人,每個(gè)項(xiàng)目,但是,至少要會(huì)用它。

13. 認(rèn)為不出錯(cuò)就是正確的

看看這個(gè)實(shí)現(xiàn)了sumOddValues功能的函數(shù),有什么問題嗎?

  1. const sumOddValues = (array) => { 
  2.   return array.reduce((accumulator, currentNumber) => { 
  3.     if (currentNumber % 2 === 1) { 
  4.       return accumulator + currentNumber; 
  5.     } 
  6.     return accumulator; 
  7.   }); 
  8. }; 
  9. console.assert( 
  10.   sumOddValues([1, 2, 3, 4, 5]) === 9 
  11. ); 

測(cè)試通過,一切順利,但情況真是如此?

上述代碼問題在于,沒有考慮到所有情況。盡管,它能正確地處理一部分的情況(測(cè)試時(shí)恰好命中這些情況之一)。來看看其中的幾個(gè)問題:

問題#1:沒有考慮輸入為空的情況。在沒有傳遞任何參數(shù)的情況下調(diào)用函數(shù),會(huì)發(fā)生什么?會(huì)出現(xiàn)如下所示的錯(cuò)誤:

  1. TypeError: Cannot read property 'reduce' of undefined. 

 

這通常是個(gè)壞兆頭,原因主要有二:

  • 用戶無法看到函數(shù)內(nèi)部,不知其如何實(shí)現(xiàn)的。
  • 異常提示對(duì)用戶沒有任何幫助,但你的函數(shù)又無法滿足用戶需求。倘若異常提示表述的更明確些,用戶就能知道自己是如何錯(cuò)誤地調(diào)用了函數(shù)。比如,可以在函數(shù)中,設(shè)計(jì)拋出一個(gè)異常,提示用戶定義出錯(cuò)了,如下所示:
    1. TypeError: Cannot execute function for empty list. 

也可以不拋出異常,忽略空輸入并返回0的總和。但是,無論如何,必須對(duì)這些情況有所處理。

問題#2:沒有處理無效輸入的情況。如果傳入的參數(shù)是字符串,整數(shù)或?qū)ο蠖皇菙?shù)組,會(huì)發(fā)生什么情況?

出現(xiàn)了下面的情況:

  1. sumOddValues(42); 
  2. TypeError: array.reduce is not a function 

那么,很不幸,因?yàn)閍rray.reduce確實(shí)是定義過的!

我們命名了函數(shù)的參數(shù)數(shù)組,因此,在函數(shù)中,將所有調(diào)用該函數(shù)的對(duì)象(42)標(biāo)記為數(shù)組。所以,就會(huì)拋出異常:42.reduce不是一個(gè)函數(shù)。

這個(gè)錯(cuò)誤很令人困惑不是?也許,更值得注意的錯(cuò)誤是:

  1. TypeError: 42 is not an array, dude. 

問題#1和#2被稱為邊緣情況,他們都是常見的邊緣案例。但通常,有一些不太明顯的邊緣案例也是需要考慮的。例如,我們傳入負(fù)數(shù),會(huì)發(fā)生什么?

  1. sumOddValues([1, 2, 3, 4, 5, -13]) // => still 9 

-13是奇數(shù),但結(jié)果是你想要的嗎?或許它應(yīng)該拋出異常?求和過程是否應(yīng)該包括參數(shù)中的負(fù)數(shù)?還是應(yīng)該忽略?也許你意識(shí)到,該函數(shù)應(yīng)命名為sumPositiveOddNumbers。

這種情況處理起來很容易,但是,更重要的一點(diǎn),如果不寫一個(gè)測(cè)試文檔來記錄測(cè)試案例的話,后續(xù)的維護(hù)者也將對(duì)此毫無線索,甚至認(rèn)為忽視負(fù)數(shù)是故意的或是出現(xiàn)了疏忽。

問題#3:測(cè)試沒有涵蓋所有的一般情況。除了邊緣情況,函數(shù)也有可能無法正確處理某個(gè)合理、有效的情況:

  1. sumOddValues([2, 1, 3, 4, 5]) // => 11 

上例中,不應(yīng)將2計(jì)入總和。

原因很簡單:reduce函數(shù)是將第二個(gè)參數(shù)作為累加器的初始值的,如果該參數(shù)為空(如代碼所示),reduce將使用數(shù)組中第一個(gè)值作為累加器的初始值。這就是為什么在上面測(cè)試用例中,第一個(gè)偶數(shù)值也包含在了總和中。

即便你在編寫的過程中就發(fā)現(xiàn)了這個(gè)問題(并解決了),也是要編寫相應(yīng)的測(cè)試案例并記錄的,測(cè)試記錄還應(yīng)包含其他測(cè)試用例,如全偶數(shù)的情況,列表中存在0的情況,列表為空的情況。

如果測(cè)試記錄很少,又忽略了很多情況,忽視了邊緣情況,那么,這一定是新手干的。

14. 對(duì)已經(jīng)存在的代碼不再質(zhì)疑

除非你是超級(jí)程序員,可以獨(dú)當(dāng)一面。否則,毫無疑問你會(huì)碰到許多愚蠢的代碼。新手往往意識(shí)不到這些,他們會(huì)認(rèn)為,既然作為代碼庫一部分,又用了很長時(shí)間的代碼,一定是沒有問題的。

更糟的是,如果這些代碼中存在不妥,新手可能就會(huì)在其他地方重復(fù)這些不妥。因?yàn)樗麄冋J(rèn)為,代碼庫中的代碼是沒有問題的,從中學(xué)到的方法也是沒有問題的。

還有一些代碼,看起來很糟糕,但是,它可能包含著某種特殊的情況,從而迫使開發(fā)人員必須這么寫。這些地方,常常會(huì)有詳細(xì)的注釋,以將情況告知給新手,并說明,代碼為何要這么寫。

作為新手,你應(yīng)該假設(shè)任何不明白或不正規(guī)的代碼都是不好的。然后,去提問,去質(zhì)疑,去查他的git blame記錄!

如果代碼的作者無處可尋,那就仔細(xì)研究代碼本身,理解其中的所有。只有當(dāng)你完全理解后,才能形成自己的觀點(diǎn)(不論好與壞)。在此之前,不要草率地對(duì)代碼下結(jié)論。

15. 沉迷于最佳實(shí)踐

我認(rèn)為“最佳實(shí)踐”這個(gè)詞著實(shí)不好,它意味著無需再深入研究,這已經(jīng)是最好的結(jié)果了,毋庸置疑!

但編程中沒有最好只有更好,只能說對(duì)某種程序而言,目前這已經(jīng)是比較好的方案了。

甚至某些我們以前認(rèn)為的最佳實(shí)踐,現(xiàn)在已經(jīng)不是最好的解決方案了。

只要你肯花時(shí)間去研究,總能發(fā)現(xiàn)更好的方案,所以不要再執(zhí)著于最佳實(shí)踐,盡你努力做到最好即可。

不要因?yàn)槟阍谀硞€(gè)地方讀到的一句名言,或是你看到別人這么做了,或是聽人說這是最佳實(shí)踐就去做某件事。

16. 沉迷于性能優(yōu)化

在編程中過早優(yōu)化是萬惡之源(至少大部分是)。

——Donald Knuth (1974年)

自從Donald Knuth發(fā)表了以上觀點(diǎn)之后,編程就發(fā)生了很大的變化,至今為止,我認(rèn)為這個(gè)觀點(diǎn)都是有價(jià)值的。

記住一條好的規(guī)則:如果你不能有效地量化代碼中的問題,那就別試圖去優(yōu)化它。

如果在執(zhí)行代碼前已經(jīng)在優(yōu)化了,那么你很可能過早的進(jìn)行了優(yōu)化,這是完全沒必要的,只是在浪費(fèi)時(shí)間。

當(dāng)然在你寫新代碼之前一些明顯需要優(yōu)化的內(nèi)容還是要考慮優(yōu)化的。例如,在Node.js中,你要確保你的代碼中沒有泛濫的使用循環(huán)或阻止調(diào)用堆棧,這些是非常重要的,這是你必須牢記要提前優(yōu)化的一個(gè)例子。所以在編寫過程中,可以時(shí)常問問自己:我準(zhǔn)備寫的代碼會(huì)阻止調(diào)用堆棧嗎?

應(yīng)該避免對(duì)任何不能量化的代碼進(jìn)行任何不明顯的優(yōu)化,否則反而會(huì)不利??赡苣阏J(rèn)為你這樣做會(huì)帶來性能上的提升,但事實(shí)上這會(huì)成為新的不可預(yù)料的bug來源。

因此,不要浪費(fèi)時(shí)間去優(yōu)化那些不能量化的性能問題。

17. 不以最終的用戶體驗(yàn)為目標(biāo)

在應(yīng)用程序中添加特性最簡單的方法是什么?從你自己的角度看,或許是看它如何適應(yīng)當(dāng)前的用戶界面,對(duì)吧?如果這個(gè)功能是要捕獲用戶輸入的,那么把它加到已有的那些表單中。如果這個(gè)功能是要添加一個(gè)頁面鏈接,那就把它加到已有的嵌套鏈接菜單中。

不要自以為是。要站在終端用戶角度來開發(fā),這樣才是真正的專業(yè)人員。這樣的開發(fā)者才會(huì)去思考有這個(gè)功能訴求的用戶需要什么,用戶又會(huì)如何操作。而且他們會(huì)考慮如何能讓用戶更便捷地找到和使用這個(gè)功能,而不是只考慮如何在應(yīng)用程序中添加這個(gè)功能,而不考慮這個(gè)功能的可發(fā)現(xiàn)性和可用性。

18. 工作時(shí)沒有選對(duì)適合的工具

每個(gè)人在完成編程的相關(guān)活動(dòng)中,都有一套自己喜歡使用的工具。其中有一些很好用,也有一些不好用,但是大多數(shù)工具只是對(duì)某一項(xiàng)特定任務(wù)很棒,而對(duì)其他任務(wù)來說都沒有那么好。

如果要把釘子釘在墻上,錘子確實(shí)是把好工具,但如果要用錘子來旋螺絲釘,那就是很糟糕的工具了。不能只是因?yàn)槟?ldquo;喜歡”錘子,就用它來旋螺絲釘。也不能因?yàn)殄N子是亞馬遜中最受歡迎的工具,用戶評(píng)價(jià)得分5.0,就用它來旋螺絲釘。

根據(jù)受歡迎程度來選擇工具,而不是針對(duì)問題的適用性來選擇工具是新手的一個(gè)標(biāo)志。

對(duì)于新手而言,另一個(gè)問題是:你也許根本不知道對(duì)一項(xiàng)特定工作來說什么工具“更好”。在你當(dāng)前的認(rèn)知范圍內(nèi),也許某一種工具就是你所知道的最好的工具。但是,跟其他工具相比時(shí),它并不是首選。你需要熟悉所有可用的工具,并且對(duì)剛開始使用的新工具保持開放的心態(tài)。

一些程序員是拒絕使用新工具的,他們對(duì)于現(xiàn)有的工具很滿意,而且他們可能也不想去學(xué)習(xí)任何新的工具。我明白,我也能理解,但是這顯然是不對(duì)的。

工欲善其事,必先利其器。你可以用原始工具建造一個(gè)小屋,并享受你的甜蜜時(shí)光;你也可以投入時(shí)間和資金去獲得好工具,這樣你就可以更快地建造一座更好的房子。工具是不斷更新的,而你也需要習(xí)慣去不斷學(xué)習(xí)并使用它們。

19. 不理解代碼問題會(huì)造成數(shù)據(jù)問題

一個(gè)程序非常重要的一方面就是某種格式數(shù)據(jù)的管理,該程序?qū)⑹翘砑有掠涗?、刪除舊記錄和修改其他記錄的界面。

程序的代碼即使有一點(diǎn)點(diǎn)的小問題,都會(huì)給其管理的數(shù)據(jù)帶來不可預(yù)估的后果,尤其當(dāng)你所有的數(shù)據(jù)驗(yàn)證都是通過那個(gè)漏洞程序完成時(shí),則更是如此。

當(dāng)涉及到代碼和數(shù)據(jù)的關(guān)系時(shí),初學(xué)者可能不會(huì)立即將這些點(diǎn)聯(lián)系起來。他們可能覺得在生產(chǎn)中繼續(xù)使用一些錯(cuò)誤代碼也是可以的,因?yàn)樘卣鱔是不用運(yùn)行的,它沒那么重要。但問題是錯(cuò)誤代碼可能會(huì)不斷地導(dǎo)致數(shù)據(jù)完整性問題,雖然這些問題在一開始的時(shí)候并不明顯。

更糟糕的是,在修復(fù)漏洞時(shí),并沒有修復(fù)漏洞所導(dǎo)致的細(xì)微的數(shù)據(jù)問題,就這樣交付代碼只會(huì)積累更多的數(shù)據(jù)問題,且這樣的問題會(huì)被貼上“不可修復(fù)”的標(biāo)簽。

那么如何避免讓自己發(fā)生這些問題呢?你可以簡單地使用多層次的數(shù)據(jù)完整性驗(yàn)證,不只依賴于單個(gè)用戶界面,應(yīng)該在前端、后端、網(wǎng)絡(luò)通信和數(shù)據(jù)庫中都創(chuàng)建驗(yàn)證。如果你不想這么做,那么請(qǐng)至少使用數(shù)據(jù)庫級(jí)別的約束。

要熟練掌握數(shù)據(jù)庫約束,并學(xué)會(huì)在數(shù)據(jù)庫中添加新列或新表時(shí)使用它們:

  • NOT NULL是對(duì)列的空值約束,表示該列不允許使用空值。如果你的應(yīng)用程序中設(shè)定某個(gè)字段必須有值,那么在數(shù)據(jù)庫中它的源數(shù)據(jù)就應(yīng)該定義為not null。
  • UNIQUE是對(duì)列的單一約束,表示在整個(gè)表中該列不允許有重復(fù)值。比如,用戶信息表的用戶姓名或者電子郵件字段,就適合使用這個(gè)約束。
  • CHECK約束是一個(gè)自定義表達(dá)式,對(duì)于滿足條件的數(shù)據(jù),計(jì)算結(jié)果為True。例如,如果有一列值必須是介于0到100之間的百分比,則可以使用CHECK約束來強(qiáng)制執(zhí)行。
  • PRINARY KEY(主鍵)約束表示某一列的值必須不為空,且不重復(fù)。你可能一直在用這個(gè)約束,數(shù)據(jù)庫中的每個(gè)表都必須有一個(gè)主鍵來識(shí)別不同的記錄。
  • FOREIGN KEY(外鍵)約束表示某一列的值必須與另一個(gè)表的某一列值相匹配,通常來說外鍵約束也會(huì)是主鍵約束。

對(duì)于新手來說,另一個(gè)與數(shù)據(jù)完整性相關(guān)的問題是缺乏對(duì)事務(wù)處理(transactions)的思考。如果多個(gè)操作需要更改同一個(gè)數(shù)據(jù)源,且它們相互依賴時(shí),則必須把它們包裝在一個(gè)事務(wù)當(dāng)中,這樣當(dāng)其中一個(gè)操作失敗時(shí)就可以進(jìn)行回滾。

20. 推倒重來

這是一件很麻煩的事情。編程過程中,有時(shí)的確是需要推倒重來。編程不是一個(gè)界限分明的領(lǐng)域,變化層出不窮,新需求提出的速度遠(yuǎn)超于任何團(tuán)隊(duì)可以應(yīng)對(duì)的能力范圍。

打個(gè)比方,基于當(dāng)前的速度,如果你需要不同種類的輪胎,除了改進(jìn)我們都熟悉且喜愛的輪胎以外,也許我們需要換一種角度思考。然而,除非真的需要特殊設(shè)計(jì)的輪胎,否則沒有必要推倒重來。就將就用用原來的輪胎吧。

不要浪費(fèi)寶貴的時(shí)間在尋找所謂的最好的輪胎之上??焖偎阉?,然后使用所尋找到的內(nèi)容。只有在這些輪胎真的沒法像宣傳的那樣好好工作時(shí),再進(jìn)行更換。

編程最酷的一件事就是大多數(shù)輪胎都是透明的,你可以看到它內(nèi)部的構(gòu)造,能夠非常容易地判斷代碼的質(zhì)量高低。

所以盡量使用開源代碼。開源包的缺陷更容易解決,更容易被替代,也更容易從內(nèi)部支持。然而,當(dāng)你需要一個(gè)輪子時(shí),不要買一個(gè)全新的車,然后把你現(xiàn)在的車放在那輛新車上。

也就是說,不要在代碼里加載一整個(gè)包,然后只使用里面的一兩個(gè)函數(shù)。最好的例子就是JavaScript中的lodash程序包。如果你只是想隨機(jī)排列一個(gè)數(shù)組,只需要加載shuffle方法就好,不要加載一整個(gè)令人絕望的lodash程序包。

21. 厭惡代碼審查

新程序員們的一個(gè)明顯特征就是把代碼審查當(dāng)做批評(píng),他們不喜歡、不珍惜,甚至是恐懼代碼審查。

大錯(cuò)特錯(cuò),如果你也有同樣的感受,那么你需要立刻改變你的態(tài)度。把每次代碼審查都看做是學(xué)習(xí)機(jī)會(huì),用開放的心態(tài)歡迎、珍惜它們,并且從中學(xué)習(xí)。更為重要的是,要向給你提供了指導(dǎo)的審查員們表示感謝。

你需要接受一個(gè)事實(shí)——每個(gè)人都是終生代碼學(xué)習(xí)者。大多數(shù)代碼審查都能教給一些你以前可能不知道的知識(shí),所以請(qǐng)將代碼審查當(dāng)做是一項(xiàng)學(xué)習(xí)資源吧。

有時(shí),審查員也會(huì)犯錯(cuò)誤,這時(shí)候就輪到你去教他們一些東西了。然而,如果審查出來的問題不僅僅是由于你的代碼導(dǎo)致的錯(cuò)誤,那么也許還是需要進(jìn)行代碼修改。如果無論如何你都需要教審查員一些東西的話,那么謹(jǐn)記:教授別人是你作為程序員最有收獲的一件事。

22. 不使用源代碼控制

新手們有時(shí)會(huì)低估一個(gè)好的源代碼/版本控制系統(tǒng),所謂好的系統(tǒng),我指的是Git。

源代碼控制并不僅僅是指把代碼修改推送給別人,然后進(jìn)行版本變更,這個(gè)行為的意義遠(yuǎn)不止如此。源代碼控制的主要目的在于清晰的歷史記錄。

代碼需要時(shí)常進(jìn)行回顧,而代碼的修改過程的記錄將會(huì)極大助力于一些疑難雜癥的解決,這也是為什么我們會(huì)很在意提交信息。代碼控制同樣也是一個(gè)溝通實(shí)施信息的渠道,使用這些零碎的提交歷史,能夠幫助未來的代碼維護(hù)人員了解代碼的發(fā)展情況以及現(xiàn)在所處的狀態(tài)。

常常提交、盡早提交,并且出于對(duì)連貫性的尊重,請(qǐng)?jiān)谔峤粯?biāo)題中使用現(xiàn)在時(shí)態(tài)。信息最好盡量詳盡,但謹(jǐn)記它們應(yīng)該是經(jīng)過提煉總結(jié)的。如果你需要好幾行來闡述想表達(dá)的內(nèi)容,也許意味著你的提交信息太長了。重來吧!

不要在提交信息中不要放入任何不必要的信息。例如,不要列出被加載、被修改或者被刪除的文件。

這些列表本身已經(jīng)包含在提交的代碼中了,并且能夠通過一些Git命令參數(shù)實(shí)現(xiàn),它們只會(huì)成為總結(jié)信息中的噪音。一些團(tuán)隊(duì)喜歡在每個(gè)文件改變中都做一次總結(jié),我認(rèn)為這是另一種提交信息太冗長的標(biāo)志。

源代碼控制和可發(fā)現(xiàn)性也有關(guān)系。當(dāng)你遇到一個(gè)函數(shù),需要開始了解它的需求或者設(shè)計(jì),你可以尋找介紹它的提交信息,然后閱讀函數(shù)相關(guān)內(nèi)容。

提交信息甚至可以幫你找到程序中導(dǎo)致缺陷的代碼是哪些。Git在提交中提供了一個(gè)二進(jìn)制搜索(bisect命令)來精準(zhǔn)定位導(dǎo)致缺陷的罪惡源頭。

源代碼控制也可以在代碼變動(dòng)正式生效之前發(fā)揮極大的作用。諸如階段轉(zhuǎn)換、選擇性打補(bǔ)丁、重置、隱藏、修復(fù)、應(yīng)用、區(qū)分、撤銷以及其他許多對(duì)代碼編輯有用的工具。所以好好理解、學(xué)習(xí)、使用并且珍惜他們吧。

你知道的Git特性越少,那么你離文章中所說的新手就越接近。

23. 過度使用共享狀態(tài)

同樣的,這一點(diǎn)并不是在比較函數(shù)式編程與其他算法的優(yōu)劣區(qū)別,那是另外一篇文章要談?wù)摰脑掝}。

需要指出的是,共享狀態(tài)往往是問題的源頭,如果可能的話,盡量避免使用它。如果無法避免,那么需要把使用共享狀態(tài)控制在最低限度。

當(dāng)我還是編程初學(xué)者的時(shí)候,我沒有意識(shí)到我們所定義的每一個(gè)變量都是一個(gè)共享狀態(tài)。變量當(dāng)中包含了數(shù)據(jù),并且可以被該變量所處的域內(nèi)所有元素改變。域的范圍越大,那么這個(gè)共享狀態(tài)的范圍就越廣。盡量把新變量聲明維持在一個(gè)小范圍內(nèi),并確保它們不會(huì)向上滲透。

情況比較嚴(yán)重的問題就是當(dāng)共享狀態(tài)生效、多個(gè)源頭都會(huì)導(dǎo)致同一個(gè)事件循環(huán)標(biāo)記發(fā)生改變時(shí)(在事件循環(huán)環(huán)境中),會(huì)發(fā)生爭用條件。

事實(shí)是:新手有可能會(huì)采取計(jì)時(shí)器作為共享狀態(tài)爭用條件的曲線救國之道,特別是當(dāng)他們需要處理數(shù)據(jù)鎖定的問題時(shí)。

這是在立flag,別這樣做。切記,處處留心,并且在代碼審查時(shí)指出這個(gè)問題,絕對(duì)不要接受這種情況。

24. 不正確地面對(duì)錯(cuò)誤

錯(cuò)誤是一個(gè)好東西,它們的存在意味著進(jìn)步,意味著你更容易獲得成長。

編程大牛們對(duì)錯(cuò)誤愛不釋手,而新手則恨之入骨。

如果看著這些可愛的小小紅色錯(cuò)誤信息,會(huì)讓你覺得心煩,那么你需要改變一下態(tài)度,把它們視為助手。你需要好好對(duì)待它們,并充分發(fā)揮它們的作用,促進(jìn)自己的成長。

有些錯(cuò)誤需要升級(jí)至異常情況。異常情況是需要你給出解決方法的用戶自定義錯(cuò)誤。有些錯(cuò)誤需要單獨(dú)進(jìn)行處理,它們的存在將會(huì)讓程序崩潰,并且強(qiáng)制退出。

25. 從不休息

程序員也是人類,你的大腦、你的身體都需要休息。常常,當(dāng)你進(jìn)入編程狀態(tài)時(shí),就忘記了休息。我把這一點(diǎn)也視為新手的一個(gè)標(biāo)志。這不是你可以妥協(xié)的點(diǎn)。把一些能夠強(qiáng)制你休息的內(nèi)容整合到你的工作流中,然后短暫地休息一下。

離開椅子,在附近走走,同時(shí)想想下面需要做的事情。當(dāng)你回到代碼的世界時(shí),就可以用全新的視角看待你的成果。

這篇文章很長,現(xiàn)在你可以休息一下了。

原文鏈接:

https://medium.com/@samerbuna/the-mistakes-i-made-as-a-beginner-programmer-ac8b3e54c312

【本文是51CTO專欄機(jī)構(gòu)大數(shù)據(jù)文摘的原創(chuàng)譯文,微信公眾號(hào)“大數(shù)據(jù)文摘( id: BigDataDigest)”】

     大數(shù)據(jù)文摘二維碼

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2025-04-29 10:17:42

2024-04-01 08:05:27

Go開發(fā)Java

2019-12-12 14:32:26

SQL語句數(shù)據(jù)庫

2011-02-21 17:51:39

Zimbra入門新手

2013-12-24 10:04:01

PostgreSQL

2018-08-01 14:45:16

PHP編程語言

2019-09-25 15:30:15

2018-10-25 15:04:22

編程程序員陷阱

2011-03-22 11:06:52

Nagios安裝

2010-07-26 10:01:01

虛擬化

2024-06-26 10:37:05

2023-03-13 13:36:00

Go擴(kuò)容切片

2018-09-11 09:14:52

面試公司缺點(diǎn)

2011-05-31 16:47:47

SEO

2011-01-10 14:36:00

新手linux基礎(chǔ)

2024-05-06 00:00:00

緩存高并發(fā)數(shù)據(jù)

2011-05-18 09:52:28

2010-06-08 16:22:20

2013-09-02 10:23:14

2022-04-26 21:49:55

Spring事務(wù)數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)