<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          初級程序員常犯錯誤都在這里啦!

          共 16887字,需瀏覽 34分鐘

           ·

          2021-04-20 15:16


          源 / 頂級程序員        文/ 曹春暉



          最近接手其他人做的項(xiàng)目,導(dǎo)致之前的一些幻想破滅了。因?yàn)閯偣ぷ鞯臅r候做項(xiàng)目是php,而php本身的web框架一般只簡單區(qū)分mvc,稍微麻煩一些的會多個library或者h(yuǎn)elper之類的。這樣分層很少有優(yōu)點(diǎn)同時也有缺點(diǎn)。當(dāng)然了,現(xiàn)代的框架一般支持namespace,你也完全可以借鑒其它語言來做自己的內(nèi)部框架。這里先不說這個。

          mvc的優(yōu)點(diǎn)自然是簡單,無論一個新人有沒有做過相關(guān)的工作,你只要跟他簡單說明每一層的職責(zé)是什么,馬上就可以開始工作。缺點(diǎn)也非常明顯,因?yàn)樘唵?,所以代碼在累積到一定量以后會變得難以控制復(fù)雜度。同時容易讓沒什么經(jīng)驗(yàn)的程序員把代碼寫得難以維護(hù)。

          所以之前聽說java的框架這方面做得好一些,然后對部門的java項(xiàng)目抱有一些幻想。不過在實(shí)際接手后,發(fā)現(xiàn)理想和現(xiàn)實(shí)真是有比較巨大的鴻溝。所以先來總結(jié)一下共通的初級程序員比較容易犯的錯誤吧。如果哪天自己帶團(tuán)隊(duì)了,面試別人也可以拿這些題作為區(qū)分人的一種界限。做項(xiàng)目的時候有思考的人和不思考的人還是會有不小的區(qū)別的。

          1、命名太啰嗦,或者不規(guī)范

          這個問題其實(shí)挺普遍的,即使是科班出身的人,工作了很多年的人,寫程序也可能冒出來一堆var a/b/c之類的變量。如果你的程序本身短小精悍,或者只是純粹的算法題,這樣似乎問題不大(反正oj ac了以后你可能也就再也不會看自己的代碼。但這樣的代碼放在生產(chǎn)環(huán)境中,問題就很大了。首先有些變量本身的生命周期可能會超過一個橫著放的屏幕。這里逗逼一下,不是誰都能買得起Dell的可橫可豎的屏幕好嗎。比如按照我的13寸筆記本來看,vim的全屏模式下只能顯示33行(當(dāng)然了,我視力不好,字體比較大)。所以如果在第一行聲明了一個變量var a。那么順著讀下去,在33行之外。。。我覺得我肯定不記得這是什么東西了。

          如果你想要聲明一個變量,一定要讓這個變量的名字本身具有含義,比如productName,productPrice,userId,userInfo,orderHistory。但保證名字有含義的同時,一定要保證名字和其含義對應(yīng)得上。并且不要把一個命名好的變量以優(yōu)化的名義反復(fù)存儲各種不同的值。例如我們這里的代碼就曾經(jīng)出現(xiàn)過看起來是userIds,但實(shí)際上打印出來卻是一堆userInfo的信息的事情。著實(shí)讓人惱火。

          業(yè)務(wù)內(nèi)部邏輯的變量命名還是講究單詞打全,意義明了等等。如果是框架本身的一些內(nèi)容命名的話。例如java的spring框架里的service命名,反而應(yīng)該稍微簡潔一些。例如:

          KnowledgeService kService;
          OrderService oService;
          UserMapper uMapper;

          因?yàn)樵谡麄€class內(nèi)部,service、mapper一般只會有唯一的對象,所以這里就算看不明白了去整個類的頭部也能找得到。因?yàn)檫@些對象使用頻率很高,所以簡短一些對打字有好處(其實(shí)你是想偷懶吧)。當(dāng)然了,如果你喜歡把名字打全,那我覺得也沒什么不可。

          變量命名之外,函數(shù)命名也很重要

          例如上一家公司就有人會把函數(shù)叫dealData或者h(yuǎn)andleData,怎么看怎么別扭。。換成formatData是不是就很明了了?細(xì)節(jié)之處見真章。

          2、魔法數(shù)字

          這個問題在哪里都看得到,最簡單的例如各種訂單的status跳轉(zhuǎn)。你會發(fā)現(xiàn)各種updateStatus(1)之類的神奇代碼。如果恰巧數(shù)據(jù)庫的表定義里又沒有這個status的定義,那必然會變成維護(hù)人員的噩夢。解決方法很簡單,在配置文件中集中維護(hù)這些特殊變量。

          再進(jìn)一步的話,應(yīng)該引入各種方便的配置系統(tǒng)。例如java里常用的disconf,可以在不對系統(tǒng)上下線的情況下在配置系統(tǒng)里看到配置的key和value,并且可以即時地進(jìn)行修改和配置下發(fā)。如果是其它語言沒有類似系統(tǒng)的話,也可以借助現(xiàn)在流行的etcd或者zookeeper來自己開發(fā),不會太復(fù)雜。

          如果你發(fā)現(xiàn)自己的系統(tǒng)里充斥著各種不明所以的數(shù)值的話,那么就是時候考慮引入配置進(jìn)行管理了。(其實(shí)從一開始就應(yīng)該做這些事情)

          3、解決了魔法數(shù)字的問題,但不對配置文件進(jìn)行分類

          有了配置系統(tǒng)以后,其實(shí)還有個麻煩的問題。就是所有配置都放在一個文件里。例如之前做的系統(tǒng),把所有key和對應(yīng)的sql都放在一起,然后導(dǎo)致配置文件變得很大很難看。想改個簡單的sql連找都找不到在哪里。

          改進(jìn)方法很簡單,第一是對配置文件進(jìn)行業(yè)務(wù)邏輯分類,例如Order相關(guān)的配置項(xiàng)就放在Order的下面。如果配置內(nèi)容還是很多,可以進(jìn)一步進(jìn)行粒度的細(xì)分,例如OrderHistory, OrderType之類的。其實(shí)一般的系統(tǒng)也不會這么復(fù)雜。

          4、分類粒度問題

          對配置進(jìn)行了分類,但有時候也有可能配置粒度實(shí)在太細(xì),導(dǎo)致文件很多,找起來還是很不方便。

          這怎么辦呢?

          第一是粒度劃分要掌握好度,第二是相關(guān)的配置最好有對應(yīng)的comment可以拿來進(jìn)行簡單的檢索。第三么,那可能就是將系統(tǒng)的模塊進(jìn)行拆分了。不同模塊管理自己的配置文件。眼不見心不煩。

          劃分之后的模塊是使用json http還是用rpc通信,看自己的業(yè)務(wù)量和延遲要求來定吧。

          5、函數(shù)寫的太長

          這也是常見的問題。業(yè)務(wù)開發(fā)在堆代碼的階段特別容易引起這種問題。例如上一家公司的createOrder邏輯,一開始只是80行的小函數(shù)。但后來陸續(xù)加入了商品內(nèi)容校驗(yàn),用戶對商品的合法性校驗(yàn)(主要是指用戶是否是跨越了購買區(qū)域啊,或者購買了本沒有資格購買的vip商品),價格校驗(yàn)(是否優(yōu)惠,優(yōu)惠了多少,前端的價格有沒有問題之類的)等等。然后就讓事情變得越來越難以收拾,80行的開頭最終變成了1000行的巨無霸。

          這個又怎么解決呢?

          首先是要根據(jù)功能進(jìn)行簡單的小函數(shù)塊劃分。

          func ABCDE() {
            A
            B
            C
            D
            E
          }

          =>

          func main() {
            A()
            B()
            C()
            D()
            E()
          }

          再具體一些

          func createOrder(userInfo UserInfo, products []Product) {
            checkUserValid(userInfo);
            checkProducts(products);
            checkUserAndProducts(userInfo, products);
            var order = insertOrder(userInfo, products);
            var createFlag = createOrderDetail(order, products);
            return createFlag;
          }

          看起來就清晰多了,如果本身有上千行,進(jìn)行劃分之后每個函數(shù)也不會太長。

          當(dāng)然了,也有人會說,我的api邏輯怎么可能這么簡單。有很多其它系統(tǒng)依賴于我當(dāng)前系統(tǒng)的數(shù)據(jù),我創(chuàng)建了訂單以后要對他們的接口進(jìn)行回調(diào)啊。這部分代碼怎么都節(jié)省不下來吧?

          其實(shí)還是可以的。

          這一“點(diǎn)”其實(shí)就是消息隊(duì)列的存在意義了。在后面的濫用回調(diào)一節(jié)中會做詳述。

          6、濫用回調(diào),增加系統(tǒng)復(fù)雜性

          濫用回調(diào)其實(shí)挺常見的,特別是在現(xiàn)在這個公司。很多時候有數(shù)據(jù)依賴的項(xiàng)目會彼此之間搞一堆錯綜復(fù)雜的回調(diào)接口。這樣在初期開發(fā)的時候因?yàn)檎{(diào)用是同步操作,所以看起來項(xiàng)目很簡單,而且同步調(diào)用隨時打日志,出了問題也容易排查。

          但依賴項(xiàng)多了以后就會變成維護(hù)和重構(gòu)的噩夢。

          舉個實(shí)際場景例子:

          這里有一個用戶的評論系統(tǒng),評論系統(tǒng)會對服務(wù)你的商家進(jìn)行一些tag勾選和內(nèi)容填空。

          對于評論系統(tǒng)本身,只需要簡單記錄被打tag和被評論的對象到mysql即可。

          但后面有信用系統(tǒng)、用戶畫像系統(tǒng)、客服系統(tǒng)依賴于這些評論的數(shù)據(jù),所以需要把這些評論tag和內(nèi)容同步給其它的幾個系統(tǒng),或者甚至是跨部門的系統(tǒng)。

          目前是怎么做的呢?通過回調(diào)。

          回調(diào)邏輯在一個接口里超過五個之后,程序員就不知道自己的代碼是干什么的了。。在現(xiàn)在互聯(lián)網(wǎng)公司每年離職率這么高的情況下還會導(dǎo)致pm和rd都離職了以后,后來的新人根本就不敢碰這些回調(diào)的問題。本來很簡單的接口,50行實(shí)現(xiàn)完成了自己的業(yè)務(wù)邏輯,但是幾百行都是在回調(diào)別人的系統(tǒng)。著實(shí)惡心。

          這個問題想要解決起來也不難,其實(shí)根本就不是該解決,這種問題在一開始就應(yīng)該極力避免??梢允褂孟㈥?duì)列來避免這個問題。我們可以回想一下人們常說的消息隊(duì)列可以用來解耦。所謂解耦,其實(shí)指的就是上面這種場景。在你的系統(tǒng)里發(fā)生了一個事件,其它系統(tǒng)對這個事件的數(shù)據(jù)有依賴,那么就讓他去訂閱你的系統(tǒng)里產(chǎn)生的消息,這條消息只要放在隊(duì)列里即可。你喜歡用kafka還是其它的消息隊(duì)列其實(shí)都是可以的。

          這樣,變化是下面這樣的:

          event A happend
          then {
              call sys A1();
              call sys A2();
              call sys A3();
              call sys A4();
              call sys A5();
              call sys A6();
              call sys A7();
              call sys A8();
              ...
          }

          =>

          event A happend
          then {
              push msg to msg queue
          }

          A1~AN subscribe topic A in msg queue

          復(fù)雜性被分散去了各自的系統(tǒng)中。看起來是不是明朗了很多。

          當(dāng)然,這種方案也不是沒有問題。一旦引入消息隊(duì)列,那么整個系統(tǒng)對消息隊(duì)列本身的可靠性就有一定的考驗(yàn)。其次,原來的同步回調(diào)變成了基于消息隊(duì)列的異步分布式系統(tǒng)。說到這種系統(tǒng),大多數(shù)人肯定會想到讓人頭痛的分布式事務(wù)。而一般分布式事務(wù)還就只是數(shù)據(jù)庫領(lǐng)域說的比較多,涉及到消息隊(duì)列的分布式事務(wù)相對則會再復(fù)雜一些。所幸的是現(xiàn)在也有一些解決這種問題的思路,雖然資料不多,感興趣的讀者可以搜索一下saga pattern。實(shí)際上即使是saga pattern也沒有完美解決這種問題。

          實(shí)際項(xiàng)目里出錯的概率其實(shí)挺低的,如果push失敗打失敗日志報警,并及時解決的話其實(shí)大多數(shù)的系統(tǒng)也沒有這么大的成本。只要在失敗時準(zhǔn)備好恢復(fù)的預(yù)案即可。例如一些兩邊系統(tǒng)進(jìn)行時間區(qū)間內(nèi)數(shù)據(jù)同步的工具。要保證一定程度的冪等。

          7、雖然有分層,但每層的結(jié)果返回沒有明確界線和定義

          這個在啥語言的項(xiàng)目里都比較多。

          比如最近接觸的java項(xiàng)目,分了entity/mapper/provider/service/controller幾層。

          其實(shí)是很科學(xué)的分層,但實(shí)際上在service這一層的使用上因?yàn)槿狈κ褂眉s束和規(guī)范,亂用的結(jié)果就是返回結(jié)果很混亂。例如有些函數(shù)是簡單地返回了查詢到的對象/對象數(shù)組,但另一些可能是直接把最終呈現(xiàn)給用戶的business status code都進(jìn)行了返回。

          這樣的話后來的維護(hù)者在接到需求的時候,難以根據(jù)直覺判斷組合哪些小函數(shù)就能實(shí)現(xiàn)新功能。只能一個一個地去看你的具體函數(shù)實(shí)現(xiàn)。

          這里似乎又體現(xiàn)出了設(shè)計模式和規(guī)范作為程序員共同語言的重要性,哈哈。

          8、明明提供的是公共接口,但是其本身卻只能使用一次

          這個錯誤。。其實(shí)很簡單,就是常說的可重入不可重入的概念,當(dāng)年剛畢業(yè)的時候我也被很多人問到,但那個時候確實(shí)沒法理解。不過其實(shí)很簡單,如果你的函數(shù)在被調(diào)用時不能保證返回結(jié)果的正確性,例如使用了全局變量且沒有加鎖,或者使用了靜態(tài)局部變量,那么這個函數(shù)就是不可重入的。但如果你的環(huán)境是php的話,這里又有一些微妙的不同。因?yàn)閜hp本身是單線程運(yùn)行,所以所謂的不可重入就只是你丫別用全局變量來存儲函數(shù)計算的結(jié)果啊。。。

          換到其它語言的話,其實(shí)就是盡量還是寫可重入的函數(shù),即使性能上稍微有些問題,但對于大多數(shù)的Web程序而言,你所謂的性能問題都是扯jb蛋。

          這里為了不扯jb蛋,我們舉個例子:

          在這個部門的XX系統(tǒng)里有這么一段php代碼:
          global $validUserList = array();

          public static getValidUserList($users) {
            global $validUserList;
            for($users as $user) {
              $validUserList[] = $user;
            }
          }

          代碼的作者很自以為聰明地使用了一個全局的validUserList的變量。然后在getValidUserList里對該global變量進(jìn)行修改。

          但是關(guān)鍵問題是。。沒有在每次調(diào)用的時候進(jìn)行初始化,導(dǎo)致了這個函數(shù)根本就只能被調(diào)用一次的問題。如果先后調(diào)用多次,那么一定會得到匪夷所思的結(jié)果。

          更關(guān)鍵的問題是,這樣的代碼竟然出現(xiàn)在線上運(yùn)行的業(yè)務(wù)系統(tǒng)里(你們真的不是一個外包團(tuán)隊(duì)嗎

          這種問題改起來很簡單,犯錯的人只能說也比較弱智了。。

          public static getValidUserList($users) {
            $validUserList = array();
            for($users as $user) {
              $validUserList[] = $user;
            }
            return $validUserList;
          }

          不要覺得每次都聲明新的數(shù)組會導(dǎo)致性能問題。你的web程序不在乎那一點(diǎn)性能。

          9、設(shè)計模式濫用

          這一點(diǎn)可能是不太好界定的一點(diǎn),用設(shè)計模式本身就是為了把重復(fù)的工作進(jìn)行一次性化。但問題很多時候不在于你用了什么設(shè)計模式,而在于寫這段代碼的人是誰。比如有人用的明明是策略模式,但你在他的代碼的字里行間都看不出來這是策略模式,只看到什么getOm(其實(shí)是莫名其妙的getObjectModel的縮寫),再通過反射去找到一個寫死了名字的xxxxfunction,再直接通過字符串進(jìn)行函數(shù)調(diào)用(php才可以這樣),讀得人云里霧里。而且待你把他的代碼全部掃過一遍之后才發(fā)現(xiàn),雖然用了策略模式,但這段代碼只有一種策略,其功能只是把數(shù)據(jù)庫里的一個表的一個字段修改為一個固定的狀態(tài)值。

          實(shí)際上只需要三行代碼就可以解決的問題。

          實(shí)際上這位程序員寫了多少代碼呢?

          1000行。。。

          你是用代碼量來衡量工作量的公司的員工嗎?

          10、訪問數(shù)據(jù)庫不做批量

          比較典型的場景,現(xiàn)在大多數(shù)的web程序都可以分為列表頁和詳情頁。。。說批量,其實(shí)主要說的就是列表頁的問題。

          例如一個商品列表頁,里面有五十種商品,每個商品有一個分類,在商品表里只存儲了分類的id,而在展示的時候需要把分類的路徑和分類的名字都展示出來。

          func getProductList(xxxx) {
             var products []Product
             for product := range products {
                categoryName := getCategoryName(product.getCategoryId())
                product.setCategoryName()
             }
          }

          這個問題看起來似乎問題不大?問題大了去了。你想想本來查詢兩次就可以解決的問題,50種商品,你就需要至少查詢51次數(shù)據(jù)庫。像php/py這樣的語言,如果沒有數(shù)據(jù)庫連接池,那每次查詢都需要去和數(shù)據(jù)庫建立連接,這個問題就更加的嚴(yán)重,本來幾毫秒的接口變成了幾秒或者幾十秒。

          即使是有連接池可以復(fù)用連接的語言,你查詢50次和2次的性能會一樣么?

          11、樹形結(jié)構(gòu)的表結(jié)構(gòu)設(shè)計問題

          公司里有人會把存儲樹形結(jié)構(gòu)的表設(shè)計成這樣:

           Field            | Type         | Null | Key | Default             | Extra                       |
          +------------------+--------------+------+-----+---------------------+-----------------------------+
          | id               | int(10)      | NO   | PRI | NULL                | auto_increment              |
          | name             | varchar(64)  | NO   |     |                     |                             |
          | parent_id        | int(10)      | NO   |     | 0                   |                             |
          | status           | tinyint(3)   | NO   |     | 0                   |                             |
          | createtime       | datetime     | NO   |     | 0000-00-00 00:00:00 |                             |
          | modifytime       | timestamp    | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
          | category_id_path | varchar(256) | NO   |     | 0                   |                             |

          先看表結(jié)構(gòu),有沒有什么問題?對,沒有存儲level信息,這種情況下如果我要找三/四級分類的所有分類id,那么就變成了一件非常麻煩惡心不可能的事情。何必呢。

          除了level之外,看起來是不是沒什么問題了,那我們來看看表里存儲的具體數(shù)據(jù):

                        id: 3459
                      name: fasdfasf
                 parent_id: 0
                    status: 0
                createtime: 2016-10-20 17:11:12
                modifytime: 2016-10-20 17:11:13
          category_id_path: 1,345,2359
          1 row in set (0.01 sec)

          發(fā)現(xiàn)什么問題了么。。

          對,category_id_path這個字段,設(shè)計者很聰明地存儲了一個分類從根到該category的完整路徑,但是卻犯了個錯誤。先賣個關(guān)子,在這樣的表里,如果你要查詢345結(jié)點(diǎn)(含)的所有子結(jié)點(diǎn)要怎么做呢?

          select * from category where category_id_path like '1,345%'

          這樣顯然是有問題的。如果一個結(jié)點(diǎn)的path是1,3456,那么不幸的,你的查詢也會把這個結(jié)點(diǎn)選出來。

          那么我是不是可以:

          select * from category where category_id_path like '1,345,'

          看起來似乎是沒問題了,那么上面要求的1,345這個結(jié)點(diǎn)怎么辦呢。。只能

          select * from category where category_id_path like '1,345,' or category_id_path = '1,345'

          了。倒是問題不大,不過為什么不在path的兩邊再加一個逗號呢?好處留給讀者去思考吧。

          12、if/else嵌套層次

          從例子開始吧:

          func createUser(user UserInfo) {
              if user.Age <= 0 {
                  inValid = true;
              } else {
                  if user.Name == "" {
                      inValid = true
                  } else {
                      if user.History.Length == 0 {
                      } else {
                          //500行
                          //500行
                          //500行
                          //500行
                          //500行
                          //500行
                          //500行
                      }
                 }
             }
             return inValid;
          }

          把邏輯正常的業(yè)務(wù)流程放在一個巨大的else里,可能是很多人的愛好。這種情況下如果你在前面的if else有十個,那你可能翻到后面連else里面是什么東西都看不到了(超出了ide的80字的紅線)。

          改起來很簡單:

          func createUser(user UserInfo) {
            if user.Age <= 0 {
              inValid = true;
              return inValid
            }
            if user.Name == "" {
              inValid = true
              return inValid
            }

            if user.History.Length == 0 {
              inValid = true;
              return inValid;
            }

            //500行
            return inValid;
          }

          這樣可以讓你的代碼神清氣爽。

          13、同一張數(shù)據(jù)庫表的查詢,每換一種查詢方式就寫一個函數(shù)

          還是我們的線上系統(tǒng),dao層有這么個mapper:

          public interface CategoryMapper xxxx {
              @Select("select * from category where name = #{name}")
              public List<Category> findByName(@Param("name") String name);
              
              @Select("select * from category where id = #{id}")
              public List<Category> findById(@Param("id") Long id);

              @Select("select * from category where parentId = #{parentId}")
              public List<Category> findByParentId(@Param("parentId") Long parentId);

              @Select("select * from category where status = #{status}")
              public List<Category> findByStatus(@Param("status") Integer status);

              //以下略
          }

          在php的系統(tǒng)里也有類似的東西,model層一張表寫了幾十個函數(shù),因?yàn)槲覀兊谋斫?jīng)常會有幾十個字段嘛。

          寫出了這些代碼的員工都是我們部門的優(yōu)秀員工哦~

          現(xiàn)在做開發(fā)應(yīng)該盡量避免這種重復(fù)勞動?,F(xiàn)在不管哪種語言,一般都會有開源的sql builder工具可以直接拿來用。這些sql builder工具一般也會有統(tǒng)一的查詢接口,支持傳入table name/order by/limit/where的map(java)/array(php)。

          這種東西即使你不用開源的,自己開發(fā)其實(shí)也花不了太久。我們前一家公司的工具就是我們的同事自己完成的,也只有兩百行左右。

          另外還可以考慮一些代碼生成器,dao層如果不是做事務(wù)、表關(guān)聯(lián)查詢的話,那很多時候幾乎不用自己去寫這方面的代碼了。

          如果你不這樣做~那么在java里會變成你的dao層噩夢。無數(shù)的重復(fù)工作的工作量啊。。不過或許老板喜歡能狂懟代碼的員工呢~

          呵呵。

          14、工作流系統(tǒng)update不判斷修改前的狀態(tài)

          工作流/訂單狀態(tài)流之類的系統(tǒng)都會有狀態(tài)流轉(zhuǎn),其實(shí)和編譯原理里的狀態(tài)機(jī)意思差不多。不同的狀態(tài)之間跳轉(zhuǎn)應(yīng)該有一個基本的先置條件,從某一個固定的狀態(tài),滿足某一些條件,然后跳轉(zhuǎn)到下一個狀態(tài)中。但實(shí)際的業(yè)務(wù)系統(tǒng),卻讓人發(fā)現(xiàn)很多系統(tǒng)在做狀態(tài)變更的時候,根本不考慮前置狀態(tài)。比如下面的代碼:

          update xxx set status = yyy where id = zzz;

          像這樣,根本不判斷前置狀態(tài)的sql實(shí)在是太多了。那么實(shí)際運(yùn)轉(zhuǎn)起來的系統(tǒng)一定不會是你想象的狀態(tài)流,出現(xiàn)了莫名其妙的跳轉(zhuǎn)你會欲哭無淚。

          為了解決這種問題很多系統(tǒng)把校驗(yàn)扔到前端去完成,風(fēng)險很大。也就是因?yàn)檫@里做的都是內(nèi)部系統(tǒng),沒有人搞破壞,所以程序員才不重視這些問題。如果你的訂單/工單狀態(tài)流和你公司的獎勵、損失密切相關(guān),那我覺得你不會覺得這些不是問題。

          解決起來很簡單,和經(jīng)常被提到的高并發(fā)時的樂觀鎖概念差不多,留給讀者去思考吧。

          15、非單線程系統(tǒng)不考慮線程安全問題

          這個問題其實(shí)說起來挺復(fù)雜的。。多線程程序里很難查的大多是這種問題,所以現(xiàn)在一般做非性能要求很高的系統(tǒng)都會盡量避免掉多線程并發(fā)?;蛘咴谠摬l(fā)的時候再并發(fā)執(zhí)行特定的任務(wù),比如java里用future或者ExecutorService之類的來進(jìn)行數(shù)據(jù)庫并發(fā)查詢,以減少串行執(zhí)行任務(wù)的等待時間。

          不過現(xiàn)在golang成為了攪局者,并發(fā)可能會成為未來的常態(tài),所幸的是,golang提供了race檢測工具,可以讓你方便的在有race的時候程序主動崩潰(233333)。這樣好歹是比C的黑暗時代進(jìn)步太多了。

          但在做開發(fā)的時候還是應(yīng)該有這樣的概念,如果有全局的map之類的變量,在訪問的時候要考慮加鎖。加了鎖還要評估性能,性能太差還得考慮是不是要參考一些concurrentMap的實(shí)現(xiàn)。點(diǎn)點(diǎn)點(diǎn)。

          是個蠻復(fù)雜的話題。

          16、open的資源不關(guān)閉,造成句柄泄露

          這個錯常由php轉(zhuǎn)其它語言的程序員來犯。我們php程序員open的東西從來不close(誤。

          如果記性不好的話,像數(shù)據(jù)庫連接池,redis連接池之類的資源都會很快被耗盡。然后你就懂了。。

          17、抱怨接口性能是語言問題

          之前有這么一個程序員,在做了一個要3s才要返回的接口之后說這是貴司php版本5.3的原因,你要是讓我換php 7肯定能快十倍。

          然后被秒打臉。就是前面說的沒有做好批量查詢的問題。

          這事情有時候確實(shí)是。。態(tài)度問題。

          大多數(shù)情況下語言對具體接口的性能影響不會有那么大,所以在你向別人這么說之前,請先簡單用日志來記錄你的程序每一步所花費(fèi)的時間為好。

          做一個聰明的程序員~

          嗯,其實(shí)我也是初級程序員。




          —  —


          一鍵三連「分享」、「點(diǎn)贊」和「在看」

          技術(shù)干貨與你天天見~


          瀏覽 39
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美成人三级在线 | 国产免费一区二区三区四区六区在线 | 午夜操逼视频网 | 五月情色天 | 国产九九传媒 |