我常常能聽到一些對話
狗腿子A:哇 我剛剛去改**項目的代碼,看的我有點懷疑人生
狗腿子B: 我如今項目的跟屎山同樣
狗腿子C: 我隔壁那哥們天天寫代碼都特別隨性,我有點按耐不住個人刀
.....
今天跟你們聊聊一些 我眼中 好孩子的編碼習慣,而不是代碼風格習慣 (psr-*
),固然仍是強烈建議你們代碼風格跟psr-4
靠齊。php
推薦一本《代碼整潔之道》,這本書我已經書都快翻爛了,牆裂推薦!!!
案例背景
有個函數須要判斷用戶是否參與活動
前端
案例代碼json
if (用戶 == VIP) { if (用戶的過時時間 <= 1個月內) { if (用戶沒參加過任務) { return true; } } else { return false; } } else { return true }
面對這種多條件的判斷能夠試着用攔截法
和逆向思惟
攔截法
只要符合條件立馬返回結果,再也不嵌套的if。能夠理解成橫向判斷變成縱向判斷。設計模式
溫馨感
從上往下看 > 從左往右看數組
逆向思惟
你們上學的時候都瞭解過,與其漫天去找符合的條件還不如找不符合條件,這樣的邏輯代碼能夠少不少。服務器
if (用戶 != VIP) { return true; } if (用戶參加過任務) { return false; } if (用戶的過時時間 <= 1個月內) { return true; } return false;
我遇到過不少項目都過分嵌套try-catch
致使最上層的try-catch
catch了寂寞。
微信
案例代碼網絡
function insertUser($data) { try { userIsInValid(); } catch (Exception $exception) { } } function userIsInValid() { try { //邏輯判斷 } catch (Exception $exception) { return true; } return true; }
這樣的代碼沒有問題,可是若是假設userIsInValid
真的發生代碼級的錯誤無法知道那裏出問題,雖然不會破壞業務的健壯性。
可能有人說了在Excetion加個日誌,可是若是嵌套的try-catch多了,排查日誌也是一件很痛苦的事情。app
1.儘量業務最上層包裹異常 除非網絡IO請求函數。
2.若是非要異常嵌套 須要定義每一個異常的類型。
3.儘量根據特定的異常進行catch 不建議直接catch Exception。
4.異常和日誌是個cp
,仍是不要忘記了。
函數
<?php function insertUser($data) { try { userIsInValid(); } catch (Exception $exception) { // 日誌 // 業務處理 } catch (HttpException $httpException) { // 日誌 // 業務處理 } } function userIsInValid() { // return true; }
案例代碼 (來源某個網民前段時間諮詢)
<?php ..... if ($code === 'NOTENOUGH') { packApiData(400014, 'Company have no enough money to pay', [], '企業餘額不足'); } elseif ($code === 'AMOUNT_LIMIT') { packApiData(400015, 'Amount limit', [], '金額超限或被微信風控攔截'); } elseif ($code === 'OPENID_ERROR') { packApiData(400016, 'Appid and Openid does not match', [], 'Openid格式錯誤或不屬於此公衆號'); } elseif ($code === 'SEND_FAILED') { // 付款錯誤,要查單來看最終結果 if ($orderInfo[1]['status'] == 'SUCCESS') { // 仍是成功給了,扣回餘額 packApiData(200, 'success', [$orderInfo[1]]); } else { packApiData(400017, 'Weixin pay failed', [], '微信支付付款失敗'); } } elseif ($code === 'SYSTEMERROR') { packApiData(400018, 'Weixin pay server error', [], '微信支付服務器錯誤'); } elseif ($code === 'NAME_MISMATCH') { packApiData(400019, 'Real name mismatch', [], '微信用戶的真名校驗失敗'); } elseif ($code === 'FREQ_LIMIT') { packApiData(400020, 'Api request frequently', [], '微信支付接口調用過於頻繁,請稍候再請求'); } elseif ($code === 'MONEY_LIMIT') { packApiData(400021, 'Company have reached total payment limit', [], '已經達到今日付款總額上限'); } elseif ($code === 'V2_ACCOUNT_SIMPLE_BAN') { packApiData(400022, 'This payment account has no real name', [], '用戶的微信支付帳戶未實名'); } elseif ($code === 'SENDNUM_LIMIT') { packApiData(400023, 'The number of times the user paid today exceeded the limit', [], '該用戶今日收款次數超過限制'); }
這樣的代碼可能寫起來特別舒服,可是後期進行業務的增長改寫和時間的沉澱,容易變成讓人懼怕的屎山代碼。
咱們用mapping錯誤碼來調整下
function packApiDataByOrderError($code) { $errorCodeMappins = [ "NOTENOUGH" => [ "code" => 400014, "wx_message" => "Company have no enough money to pay", "error_message" => "企業餘額不足" ], "AMOUNT_LIMIT" => [ "code" => 400015, "wx_message" => "Amount limit", "error_message" => "金額超限或被微信風控攔截" ], ..... ]; if (array_key_exists($code, $errorCodeMappins)) { packApiData( $errorCodeMappins[$code]['code'], $errorCodeMappins[$code]['wx_message'], [], $errorCodeMappins[$code]['error_message'] ); } packApiData( 999999, "undefined message", [], "未知錯誤" ); }
建議errorCodeMappins
不要放在函數內,能夠放在類頂部或者專門枚舉類。
經過errorCode 能夠避免調整主流程代碼,可以保證主流程的代碼比較精簡也能對不一樣的code進行錯誤的定義
if ($code == "SEND_FAILED") { // 付款錯誤,要查單來看最終結果 if ($orderInfo[1]['status'] == 'SUCCESS') { // 仍是成功給了,扣回餘額 PDOQuery($dbcon, 'UPDATE user SET money=money-? WHERE open_id=?', [$payAmount, $openId], [PDO::PARAM_INT, PDO::PARAM_STR]); packApiData(200, 'success', [$orderInfo[1]]); } else { packApiData(400017, 'Weixin pay failed', [], '微信支付付款失敗'); } } packApiDataByOrderError($code);
上述可能只能針對錯誤碼進行改造,若是萬一咱們須要不一樣的錯誤進行邏輯處理還怎麼辦。這時候能夠考慮用設計模式 (好比用以多態取代條件表達式)
設計模式
固好但不要過分使用,否則整個項目更難維護,你要堅信將來的你隊友不知道是什麼樣的生物
$callbackCodeMappings = [ "SEND_FAILED" => OrderSendFailed::class, ]; if (array_key_exists($code, $callbackCodeMappings)) { $class = new $callbackCodeMappings[$code]; $class->handle(); } interface OrderStateImp { public function handle($context); } class OrderSendFailed implements OrderStateImp { public function handle($context) { } }
$callbackCodeMappings
一樣建議配置專門枚舉文件內。
給出得代碼比較粗糙,其實能夠更加健壯性的作一些判斷
寫接口的咱們對如下的json格式特別熟悉
{ "success": true, "error_code": 0, "message": "", "results": [] }
對如下的代碼也已經熟悉
if (***) { $this->error(999,"****", []); }
這樣的結果的錯誤碼容易重複沒有統一管理,事實上惟一錯誤碼應該有如下幫助。
1.前端能夠根據錯誤碼作邏輯處理
2.根據錯誤碼能直接快速定位到錯誤代碼
建議
<?php namespace App\ErrorCode; class UserErrorCode { const USER_DISABLE_ERROR = [ "error_code" => 1050001, "message" => "用戶已被停用" ]; } $this->error(UserErrorCode::USER_DISABLE_ERROR);
錯誤碼建議
1-2位 - 項目碼 | 3-4位 - 模塊碼 | 5-7位具體業務錯誤碼
不可靠的命名總會讓人誤導。
好比變量命名爲userArrayList 我覺得是個數組列表變量,事實上這個特麼是個對象列表。
1.作有意義的區分
好比 singleUserItem
跟userItem
有啥區別
好比 getUserList
跟getUsers
有啥區別
2.能夠經過搜索翻譯
能知道的變量含義
不要把變量貼入搜索翻譯
會出現七七八八的東西
3.若是真的不知道該怎麼翻試試用拼音把別硬凹了
好比以前作百度
的一個接口對接
變量命名爲hundredDegree
而不是baidu
其餘的能夠參照《代碼簡潔之道》
最最最最後也是最重要的,代碼的噁心大多數來源於函數的職責不清晰,有全都塞在一塊兒的、東一塊西一塊的。
其實關於單一職責有不少文章在描述,如何去檢驗或者去寫符合標準的單一職責。
畫流程圖
若是你能把業務的流程圖畫的特別清晰,那麼你的函數的職責也就定下來了。
<?php // 兌換邏輯 function doExchange() { if (checkIsLock()) { } lock(); if (!checkUserIsExchange()) { } costUserPoint(); exchangeGoods(); } // 判斷是否悲觀鎖 function checkIsLock(){} // 上悲觀鎖 function lock(){} // 判斷用戶是否能夠兌換 function checkUserIsExchange(){} // 扣除積分 function costUserPoint(){} // 兌換商品 function exchangeGoods(){}
上述爲洪光光
心中的好孩子的習慣,也有多是你眼中壞孩子的習慣。若是你認爲是壞孩子的習慣或者認爲還有其餘好孩子的習慣歡迎評論撕逼討論。
畢竟