程序員你爲何這麼累 | 編碼規範

導讀

你們一提到程序員,首先想到的是如下標籤:苦逼,加班,熬夜通宵。可是,但凡工做了的同窗都知道,其實大部分程序員作的事情都很簡單,代碼 CRUD 能夠說毫無技術含量,就算什麼不懂依葫蘆畫瓢不少功能也能勉強作出來,作個多線程併發就算高科技了,程序員這行的門檻其實仍是比較低的。(這裏說的是大部分,有些牛逼的,寫算法、jvm 等的請自動跳過)javascript

是否是以爲很矛盾,一方面工做不復雜,一方面卻累成狗。有沒有想過問題出在哪裏?有沒有想過期間都花在哪裏呢?java

對於我我的來講,編碼仍是一個相對輕鬆的活(我是負責公司it系統的,沒有太多技術含量,數據量大,但併發量不大)。從工做到如今,我加班編碼的時間仍是比較少的,我到如今爲止天天還會編碼,不多由於編碼工做加班。git

你們寫的東西都是一些 CURD 的業務邏輯代碼,爲何你們這麼累,加班加點每天都是奮鬥者?我從本身帶的項目中觀察中發現,大部分人的大部分時間都是在 定位問題 + 改代碼,真正開發的時間並很少。定位問題包括開發轉測試的時候發現問題和上線後發現問題,改代碼的包括改 bug 和由於需求變更修改代碼(後面專門開一貼說如何應對需求改動)。程序員

因此說,simple is not easy。不少人就是由於以爲簡單,因此功能完成本身測試 ok 了就算了,沒有思考有沒有更加好的方式。歸根究竟是由於編碼習慣太糟糕,寫的代碼太爛,致使沒法定位頻繁修改頻繁出問題。(後面我會詳細講一些我看到的大部分的編碼問題。)github

其實,對於我的來講,技術很重要,可是對於工做來講,編碼的習慣比技術更加主要。工做中你面試的大部分技術都不須要用到的。工做中,由於你的編碼習慣很差,寫的代碼質量差,代碼冗餘重複多,不少無關的代碼和業務代碼攪在一塊兒,致使了你疲於奔命應付各類問題。面試

因此我做爲 SE,無論接手任何項目組,第一步就是制定代碼框架,制定項目組的開發規範,把代碼量減下去。事實上證實,這一步以後,你們的代碼量能下去最少1/3,後臺的問題數降低比較明顯,你們的加班會比以前少。ajax

給你們一個直觀的例子。下面是 controller 的一個刪除數據的接口,我來以前你們寫的這個樣子的(其實一開始比這個還差不少),功能很簡單,輸入一個對象id執行刪除返回是否刪除成功。你們有沒有以爲有什麼問題?算法

@PostMapping("/delete")
public Map<String, Object> delete(long id, String lang) {
  Map<String, Object> data = new HashMap<String, Object>();

  boolean result = false;
  try {
    // 語言(中英文提示不一樣)
    Locale local = "zh".equalsIgnoreCase(lang) ? Locale.CHINESE : Locale.ENGLISH;

    result = configService.delete(id, local);

    data.put("code", 0);

  } catch (CheckException e) {
    // 參數等校驗出錯,這類異常屬於已知異常,不須要打印堆棧,返回碼爲-1
    data.put("code", -1);
    data.put("msg", e.getMessage());
  } catch (Exception e) {
    // 其餘未知異常,須要打印堆棧分析用,返回碼爲99
    log.error(e);

    data.put("code", 99);
    data.put("msg", e.toString());
  }

  data.put("result", result);

  return data;
}

其實上面的代碼也沒有大問題。而我接手以後,我會開發本身的代碼框架,最後制定代碼框架交付的代碼以下(這是 controller 的部分):編程

@PostMapping("/delete")
public ResultBean<Boolean> delete(long id) {
  return new ResultBean<Boolean>(configService.delete(id));
}

用到的技術就是 AOP,也不是什麼高深技術。怎麼樣?代碼量就一行,特性一個都沒有丟。這就是咱們項目組如今的 controller 的樣子!(若是剛好有我帶過的項目組的人,看到 ResultBean<> 應該很熟悉應該知道我是誰了)json

因此說技術無所謂高低,看你怎麼樣用。上面的代碼簡單說一下問題,第一,lang 和業務沒有什麼關係,我後面的代碼框架去掉了(不是說我後面的代碼沒有這個功能,是把他隱藏起來對開發人員透明瞭,使用的技術就是 ThreadLocal )。第二,前面那個代碼,實際上幹活的就只有一行,其餘都和業務代碼沒有一毛錢關係,個人代碼框架裏面徹底看不到了。

使用的技術真的很簡單,可是編碼效果很是好,由於你們不要由於使用的技術初級就以爲不重要!!使用這套框架後,你們不再須要大部分時間都寫一些無聊的代碼,能夠有更加多時間學習其餘技術。說實話,在我項目組的開發人員都是比較幸運的,以爲能學到東西,不是像其餘項目組,寫了幾年都是同樣的 CRUD 代碼,雖然我比較嚴厲,可是仍是願意待在我項目組,畢竟加班比其餘項目組少啊。

這就是我說的工做中,編碼習慣(或者說編碼風格)比技術更加劇要。我工做了也有很長時間了,我以爲我我的價值最大的地方就是這些,技術上其實我懂的也和你們差很少,但編碼上我仍是以爲能夠超過大部分人的。後面我會把咱們這些業務系統中你們編碼的問題一個一個寫出來,並把個人解決辦法分享出來。謝謝你們關注!

接口定義常見問題

工做中,少不了要定義各類接口,系統集成要定義接口,先後臺掉調用也要定義接口。接口定義必定程度上能反應程序員的編程功底。列舉一下工做中我發現你們容易出現的問題:

返回格式不統一

同一個接口,有時候返回數組,有時候返回單個;成功的時候返回對象,失敗的時候返回錯誤信息字符串。工做中有個系統集成就是這樣定義的接口,真是辣眼睛。這個對應代碼上,返回的類型是map,json,object,都是不該該的。實際工做中,咱們會定義一個統一的格式,就是 ResultBean,分頁的有另一個 PageResultBean

錯誤範例
返回 map 可讀性很差,不知道里面是什麼

@PostMapping("/delete")
public Map<String, Object> delete(long id, String lang) {

}

錯誤範例
返回 map 可讀性很差,不知道里面是什麼

@PostMapping("/delete")
public Object delete(long id, String lang) {
  try {
    boolean result = configService.delete(id, local);
    return result;
  } catch (Exception e) {
    log.error(e);
    return e.toString();
  }
}

沒有考慮失敗狀況

一開始只考慮成功場景,等後面測試發現有錯誤狀況,怎麼辦,改接口唄,先後臺都改,勞民傷財無用功。

錯誤範例
不返回任何數據,沒有考慮失敗場景,容易返工

@PostMapping("/update")
public void update(long id, xxx) {

}

出現和業務無關的輸入參數

如lang語言,當前用戶信息 都不該該出現參數裏面,應該從當前會話裏面獲取。後面講ThreadLocal 會說到怎麼樣去掉。除了代碼可讀性很差問題外,尤爲是參數出現當前用戶信息的,這是個嚴重問題。

錯誤範例
(當前用戶刪除數據)參數出現lang和userid,尤爲是userid,大忌

@PostMapping("/delete")
public Map<String, Object> delete(long id, String lang, String userId) {

}

出現複雜的參數

通常狀況下,不容許出現例如 json 字符串這樣的參數,這種參數可讀性極差。應該定義對應的 bean。

錯誤範例
參數出現json格式,可讀性很差,代碼也難看

@PostMapping("/update")
public Map<String, Object> update(long id, String jsonStr) {

}

沒有返回應該返回的數據

例如,新增接口通常狀況下應該返回新對象的 id 標識,這須要編程經驗。新手定義的時候由於前臺沒有用就不返回數據或者只返回true,這都是不恰當的。別人要不要是別人的事情,你該返回的仍是應該返回。

錯誤範例
約定俗成,新建應該返回新對象的信息(對象或者 ID ),只返回 boolean 容易致使返工

@PostMapping("/add")
public boolean add(xxx) {
  //xxx
  return configService.add();
}

不少人看了個人這篇文章程序員你爲何這麼累?,都以爲裏面的技術也很簡單,沒有什麼特別的地方,可是,實現這個代碼框架以前,就是要你的接口的統一的格式 ResultBean,aop 纔好作。有些人誤解了,我那篇文章說的都不是技術,重點說的是編碼習慣工做方式,若是你重點仍是放在什麼技術上,那我也幫不了你了。一樣,若是我後面的關於習慣和規範的帖子,你重點仍是放在技術上的話,那是丟了西瓜撿芝麻,有不少貼仍是沒有任何技術點呢。

總結

統一的接口規範,能幫忙規避不少無用的返工修改和可能出現的問題。能使代碼可讀性更加好,利於進行aop和自動化測試這些額外工做。你們必定要重視。

Controller規範

第一篇文章中,我貼了2段代碼,第一個是原生態的,第2段是我指定了接口定義規範,使用AOP技術以後最終交付的代碼,從15行到1行,本身感覺一下。今天來講說你們關注的AOP如何實現。

先說說Controller規範,主要的內容是就是接口定義裏面的內容,你只要遵循裏面的規範,controller就問題不大,除了這些,還有另外的幾點.。

統一返回ResultBean對象

全部函數返回統一的 ResultBean/PageResultBean 格式,緣由見個人接口定義這個貼。沒有統一格式,AOP 沒法玩,更加劇要的是前臺代碼很很差寫。固然類名你能夠按照本身喜愛隨便定義,如就叫Result。

你們都知道,前臺代碼很難寫好作到重用,而咱們返回相同數據結構後,前臺代碼能夠這樣寫(方法 handlerResult 的重用):

// 查詢全部配置項記錄
function fetchAllConfigs() {
  $.getJSON('config/all', function(result) {
    handlerResult(result, renderConfigs);
  });
}

// 根據id刪除配置項
function deleteConfig(id) {
  $.post('config/delete', {
    id : id
  }, function(result) {
    console.log('delete result', result);
    handlerResult(result, fetchAllConfigs);
  });
}

/**
  * 後臺返回相同的數據結構,前臺的代碼纔好寫才能重用
  * @param result: ajax返回的結果
  * @param fn: 成功的處理函數(傳入data)
  */
function handlerResult(result, fn) {
  // 成功執行操做,失敗提示緣由
  if (result.code == 0) {
    fn(result.data);
  }
  // 沒有登錄異常,重定向到登錄頁面
  else if (result.code == -1) {
    showError("沒有登陸");
  }
  // 參數校驗出錯,直接提示
  else if (result.code == 1) {
    showError(result.msg);
  }
  // 沒有權限,顯示申請權限電子流
  else if (result.code == 2) {
    showError("沒有權限");  
  } else {
    // 不該該出現的異常,應該重點關注
    showError(result.msg);
  }
}

ResultBean不容許日後傳

ResultBean/PageResultBean是controller專用的,不容許日後傳!往其餘地方傳以後,可讀性立馬降低,和傳map,json好不了多少。

Controller只作參數格式的轉換

Controller作參數格式的轉換,不容許把json,map這類對象傳到services去,也不容許services返回json、map。寫過代碼都知道,map,json這種格式靈活,可是可讀性差( 編碼一時爽,重構火葬場)。若是放業務數據,每次閱讀起來都十分困難,須要從頭至尾看完才知道里面有什麼,是什麼格式。定義一個bean看着工做量多了,但代碼清晰多了。

參數不容許出現Request,Response 這些對象

和json/map同樣,主要是可讀性差的問題。通常狀況下不容許出現這些參數,除非要操做流。

不須要打印日誌

日誌在AOP裏面會打印,並且個人建議是大部分日誌在Services這層打印。

建議

Contorller只作參數格式轉換,若是沒有參數須要轉換的,那麼就一行代碼。日誌/參數校驗/權限判斷建議放到service裏面,畢竟controller基本沒法重用,而service重用較多。而咱們的單元測試也不須要測試controller,直接測試service便可。

規範裏面大部分是 不要作的項多,要作的比較少,落地比較容易。

原文:xwjie

相關文章
相關標籤/搜索