函數(shù)編寫建議

傻瓜都能寫出計(jì)算機(jī)可以讀懂的代碼监嗜,只有優(yōu)秀的程序員才能寫出人能讀懂的代碼蒋畜!

在我看來(lái)饶号,編寫簡(jiǎn)單的函數(shù)是一件簡(jiǎn)單又困難的事情。簡(jiǎn)單是因?yàn)檫@沒(méi)有什么技術(shù)難點(diǎn)咳燕,困難是因?yàn)檫@是一種思維習(xí)慣勿决,很難養(yǎng)成,不寫個(gè)幾年代碼招盲,很難寫出像樣的代碼低缩。

大部分的程序員寫的都是CRUD、一些業(yè)務(wù)邏輯的代碼曹货,誰(shuí)實(shí)現(xiàn)不了咆繁?對(duì)于我來(lái)說(shuō),如果業(yè)務(wù)邏輯的代碼評(píng)審顶籽,需要人來(lái)講每一個(gè)代碼做了什么玩般,這樣的代碼就是不合格的,合格的代碼寫出來(lái)應(yīng)該像人說(shuō)話那么簡(jiǎn)單有條理礼饱,基本上是業(yè)務(wù)怎么樣描述需求坏为,寫出來(lái)的代碼就是怎么樣的究驴。編寫出非開(kāi)發(fā)人員都能看懂的代碼,才是我們追求的目標(biāo)匀伏。不要以寫出了一些非常復(fù)雜的代碼而沾沾自喜洒忧。好的代碼應(yīng)該是看起來(lái)平淡無(wú)奇覺(jué)得很簡(jiǎn)單自然,而不是看得人云里霧里的覺(jué)得很高深很有技術(shù)含量够颠。

如果你做好了我前面幾篇文章的要求熙侍,編寫簡(jiǎn)單的函數(shù)就容易的多,如果你覺(jué)得我之前說(shuō)的去掉local履磨,去掉用戶參數(shù)這些沒(méi)有什么必要是小題大做蛉抓,那么我覺(jué)得你寫不出簡(jiǎn)單的函數(shù)。從個(gè)人經(jīng)驗(yàn)來(lái)說(shuō)蹬耘,函數(shù)編寫的建議有以下幾點(diǎn):

1 不要出現(xiàn)和業(yè)務(wù)無(wú)關(guān)的參數(shù)
參考我之前的帖子芝雪,參數(shù)校驗(yàn)和國(guó)際化規(guī)范,函數(shù)參數(shù)里面不要出現(xiàn)local综苔,messagesource惩系,request,Response這些參數(shù)如筛,第一非常干擾閱讀堡牡,一堆無(wú)關(guān)的參數(shù)把業(yè)務(wù)代碼都遮掩住了,第二導(dǎo)致你的函數(shù)不好測(cè)試杨刨,如你要構(gòu)建一個(gè)request參數(shù)來(lái)測(cè)試晤柄,還是有一定難度的。

2 避免使用Map妖胀,Json這些復(fù)雜的數(shù)據(jù)對(duì)象作為參數(shù)和結(jié)果
這類參數(shù)看著靈活方便芥颈,但是靈活的同義詞(代價(jià))就是復(fù)雜,最終的結(jié)果是可變數(shù)多赚抡、bug多爬坑、質(zhì)量差。就好比刻板的同義詞就是嚴(yán)謹(jǐn)涂臣,最終的結(jié)果就是高質(zhì)量盾计。千萬(wàn)不要為了偷懶少幾行代碼,就到處把map赁遗,json傳來(lái)傳去署辉。其實(shí)定義一個(gè)bean也相當(dāng)簡(jiǎn)單,加上lombok之后岩四,代碼量也沒(méi)有幾行哭尝,但代碼可讀性就不可同日而語(yǔ)了。做過(guò)開(kāi)發(fā)的人應(yīng)該很容易體會(huì)炫乓,你如果接手一個(gè)項(xiàng)目刚夺,到處的輸入輸出都是map的話献丑,request從頭傳到尾,看到這樣的代碼你會(huì)哭的侠姑,我相信你會(huì)馬上崩潰很快離職的创橄。
還有人說(shuō)用bean的話后面加字段改起來(lái)麻煩,你用map還不是一樣要加一個(gè)key莽红,不是更加麻煩嗎妥畏?說(shuō)到底就是懶!

如果一個(gè)項(xiàng)目的所有代碼都如下面這樣安吁,我是會(huì)崩潰的醉蚁!

/**
 * !9淼辍网棍!錯(cuò)誤代碼示例
 * 1. 和業(yè)務(wù)無(wú)關(guān)的參數(shù)locale,messagesource
 * 2. 輸入輸出都是map妇智,根本不知道輸入了什么滥玷,返回了什么
 * 
 * @param params
 * @param local
 * @param messageSource
 * @return
 */
public Map<String, Object> addConfig(Map<String, Object> params, 
    Locale locale, MessageSource messageSource) {

  Map<String, Object> data = new HashMap<String, Object>();

  try {
    String name = (String) params.get("name");
    String value = (String) params.get("value");
    
    //示例代碼,省略其他代碼
  }
  catch (Exception e) {
    logger.error("add config error", e);

    data.put("code", 99);
    data.put("msg", messageSource.getMessage("SYSTEMERROR", null, locale));
  }

  return data;
}

3 有明確的輸入輸出和方法名

盡量有清晰的輸入輸出參數(shù)巍棱,使人一看就知道函數(shù)做了啥惑畴。舉例:

public void updateUser(Map<String, Object> params){
  long userId = (Long) params.get("id");
  String nickname = (String) params.get("nickname");
  
  //更新代碼
}

上面的函數(shù),看函數(shù)定義你只知道更新了用戶對(duì)象航徙,但你不知道更新了用戶的什么信息如贷。建議寫成下面這樣:

public void updateUserNickName(long userId, String nickname){
  //更新代碼
}

你就算不看方法名,只看參數(shù)就能知道這個(gè)函數(shù)只更新了nickname一個(gè)字段到踏。多好案芨ぁ!
這點(diǎn)只可意會(huì)不可言傳窝稿。

4 把可能變化的地方封裝成函數(shù)

編寫函數(shù)的總體指導(dǎo)思想是抽象和封裝霞掺,你要把代碼的邏輯抽象出來(lái)封裝成為一個(gè)函數(shù),以應(yīng)對(duì)將來(lái)可能的變化讹躯。以后代碼邏輯有變更的時(shí)候,單獨(dú)修改和測(cè)試這個(gè)函數(shù)即可缠劝。

這一點(diǎn)相當(dāng)重要潮梯,否則你會(huì)覺(jué)得怎么需求老變?改代碼煩死了惨恭。

如何識(shí)別可能變的地方秉馏,多思考一下就知道了,工作久了就知道了脱羡。比如萝究,開(kāi)發(fā)初期免都,業(yè)務(wù)說(shuō)只有管理員才可以刪除某個(gè)對(duì)象,你就應(yīng)該考慮到后面可能除了管理員帆竹,其他角色也可能可以刪除绕娘,或者說(shuō)對(duì)象的創(chuàng)建者也可以刪除,這就是將來(lái)潛在的變化栽连,你寫代碼的時(shí)候就要埋下伏筆险领,把是否能刪除做成一個(gè)函數(shù)。后面需求變更的時(shí)候秒紧,你就只需要改一個(gè)函數(shù)绢陌。

舉例,刪除配置項(xiàng)的邏輯熔恢,判斷一下只有是自己創(chuàng)建的配置項(xiàng)才可以刪除脐湾,一開(kāi)始代碼是這樣的:

/**
 * 刪除配置項(xiàng)
 */
@Override
public boolean delete(long id) {
  Config config = configs.get(id);
  
  if(config == null){
    return false;
  }
  
  // 只有自己創(chuàng)建的可以刪除
  if (UserUtil.getUser().equals(config.getCreator())) {
    return configs.remove(id) != null;      
  }
  
  return false;
}

這里我會(huì)識(shí)別一下,是否可以刪除這個(gè)地方就有可能會(huì)變化叙淌,很有可能以后管理員就可以刪除任何人的秤掌,那么這里就抽成一個(gè)函數(shù):

/**
 * 刪除配置項(xiàng)
 */
@Override
public boolean delete(long id) {
  Config config = configs.get(id);
  
  if(config == null){
    return false;
  }
  
  // 判斷是否可以刪除
  if (canDelete(config)) {
    return configs.remove(id) != null;      
  }
  
  return false;
}

/**
 * 判斷邏輯變化可能性大,抽取一個(gè)函數(shù)
 * 
 * @param config
 * @return
 */
private boolean canDelete(Config config) {
  return UserUtil.getUser().equals(config.getCreator());
}

后來(lái)想了一下凿菩,沒(méi)有權(quán)限應(yīng)該拋出異常机杜,再次修改為:

/**
 * 刪除配置項(xiàng)
 */
@Override
public boolean delete(long id) {
  Config config = configs.get(id);

  if (config == null) {
    return false;
  }

  // 判斷是否可以刪除
  check(canDelete(config), "no.permission");

  return configs.remove(id) != null;
}

這就是簡(jiǎn)單的抽象和封裝的藝術(shù)⌒乒龋看這些代碼椒拗,參數(shù)多么的簡(jiǎn)單,很容易理解吧获黔。

這一點(diǎn)非常重要蚀苛,做好了這點(diǎn),大部分的小的需求變更對(duì)程序員的傷害就會(huì)降到最低了玷氏!畢竟需求變更大部分都是這些小邏輯的變更堵未。

5 編寫能測(cè)試的函數(shù)

程序猿不招妹子們喜愛(ài)的根本原因在于追求了錯(cuò)誤的目標(biāo):更短、更小盏触、更快渗蟹。
這個(gè)非常重要,當(dāng)然很難實(shí)現(xiàn)赞辩,很多人做技術(shù)之前都覺(jué)得代碼都會(huì)做單元測(cè)試雌芽,實(shí)際上和業(yè)務(wù)相關(guān)的代碼單元測(cè)試是很難做的。
我覺(jué)得要編寫能測(cè)試的函數(shù)主要有以下幾點(diǎn):
第一不要出現(xiàn)亂七八糟的參數(shù)辨嗽,如參數(shù)里面有request世落,response就不好測(cè)試,
第二你要把函數(shù)寫小一點(diǎn)糟需。如果一個(gè)功能你service代碼只有一個(gè)函數(shù)屉佳,那么你想做單元測(cè)試是很難做到的谷朝。我的習(xí)慣是盡量寫小一點(diǎn),力求每一個(gè)函數(shù)都可以單獨(dú)測(cè)試(用junit測(cè)試或者main函數(shù)測(cè)試都沒(méi)有關(guān)系)武花。這樣會(huì)節(jié)約大量的時(shí)間圆凰,尤其是代碼頻繁改動(dòng)的時(shí)候。我們應(yīng)用重啟一次需要15分鐘以上髓堪。新手可以寫一個(gè)功能可能需要重啟10幾次送朱,我可能只需要重啟幾次,節(jié)約的時(shí)候的很可觀的干旁。
第三你要有單獨(dú)測(cè)試每一個(gè)函數(shù)的習(xí)慣驶沼。不要一上來(lái)就測(cè)試整個(gè)功能,應(yīng)該一行一行代碼争群、一個(gè)一個(gè)函數(shù)測(cè)試回怜,有了這個(gè)習(xí)慣,自然就會(huì)寫出能測(cè)試的小函數(shù)换薄。所以說(shuō)玉雾,只有喜歡編碼的人才能寫出好代碼。

配置規(guī)范這篇文章了轻要,我的配置相關(guān)代碼复旬,都是可以單獨(dú)測(cè)試的,所以配置項(xiàng)的改動(dòng)不需要測(cè)試業(yè)務(wù)功能冲泥,應(yīng)用都不需要重啟驹碍。

====================總結(jié)======================
一句話總結(jié),使用簡(jiǎn)單參數(shù)凡恍,不要出現(xiàn)和業(yè)務(wù)無(wú)關(guān)的參數(shù)志秃,把可能變化的地方都寫出獨(dú)立的函數(shù),力求每一個(gè)函數(shù)都可以單獨(dú)測(cè)試嚼酝。

上一篇:《參數(shù)校驗(yàn)和國(guó)際化規(guī)范》
下一篇:《未完成》

本文原著作者:曉風(fēng)輕
原文鏈接:https://zhuanlan.zhihu.com/p/29129469
版權(quán)歸作者所有浮还,轉(zhuǎn)載請(qǐng)注明出處

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市闽巩,隨后出現(xiàn)的幾起案子钧舌,更是在濱河造成了極大的恐慌,老刑警劉巖涎跨,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件延刘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡六敬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門驾荣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)外构,“玉大人普泡,你說(shuō)我怎么就攤上這事∩蟊啵” “怎么了撼班?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)垒酬。 經(jīng)常有香客問(wèn)我砰嘁,道長(zhǎng),這世上最難降的妖魔是什么勘究? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任矮湘,我火速辦了婚禮,結(jié)果婚禮上口糕,老公的妹妹穿的比我還像新娘缅阳。我一直安慰自己,他們只是感情好景描,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布十办。 她就那樣靜靜地躺著,像睡著了一般超棺。 火紅的嫁衣襯著肌膚如雪向族。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天棠绘,我揣著相機(jī)與錄音件相,去河邊找鬼。 笑死弄唧,一個(gè)胖子當(dāng)著我的面吹牛适肠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播候引,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼侯养,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了澄干?” 一聲冷哼從身側(cè)響起逛揩,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎麸俘,沒(méi)想到半個(gè)月后辩稽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡从媚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年逞泄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喷众,死狀恐怖各谚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情到千,我是刑警寧澤昌渤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站憔四,受9級(jí)特大地震影響膀息,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜了赵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一潜支、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斟览,春花似錦毁腿、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至妓羊,卻和暖如春胯究,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背躁绸。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工裕循, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人净刮。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓剥哑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親淹父。 傳聞我的和親對(duì)象是個(gè)殘疾皇子株婴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容