前言
本次實踐是對一個現(xiàn)有的app的項目的后臺進行代碼規(guī)范,通過對比來實現(xiàn)bad code 到 good code 的一次實踐
目的是提高代碼的可讀性
關(guān)于規(guī)范
- 高效編碼 - 避免了過多的選擇造成的『決策時間』浪費;
- 風格統(tǒng)一 - 最大程度統(tǒng)一了開發(fā)團隊成員代碼書寫風格和思路,代碼閱讀起來如出一轍;
- 減少錯誤 - 減小初級工程師的犯錯幾率。
開發(fā)哲學
DRY –「Don’t Repeat Yourself」不寫重復(fù)的邏輯代碼;
約定俗成 - 「Convention Over Configuration」,優(yōu)先選擇框架提倡的做法,不過度配置;
KISS - 「Keep it Simple, Stupid」提倡簡單易讀的代碼,不寫高深、晦澀難懂的代碼,不過度設(shè)計;
主廚精選 - 讓有經(jīng)驗的人來為你選擇方案,不獨創(chuàng)方案;
官方提倡 - 優(yōu)先選擇官方推崇的方案
感受
在一個項目中如果出現(xiàn)風格不統(tǒng)一的寫法,即使幾種寫法你都能明白,也是規(guī)則允許的寫法,即便是單人開發(fā)的項目,你在閱讀過程中,也會因為這個原因出現(xiàn)短暫的停頓或者在寫代碼時會出現(xiàn)一個小的決策選擇,所以風格的統(tǒng)一是非常重要的,尤其是多人開發(fā)的項目中,更應(yīng)該注意開發(fā)規(guī)范。
與其無休止的爭論哪種選項最好,還不如只知道 一種選項。這 一種選項 能覆蓋大部分的用例,且兼?zhèn)溟_發(fā)效率、程序執(zhí)行效率、擴展性、安全性等最佳實踐,當再次遇到此類需求時,毫不猶豫地使用這 一種選項 直接了當?shù)亟鉀Q問題。
決策已提前做好,沒必要浪費時間多次決策,節(jié)省時間,提高效率。
開發(fā)規(guī)范一旦統(tǒng)一,所有團隊成員嚴格遵守,你會發(fā)現(xiàn),你的隊友寫的代碼就如你自己寫的一樣,編碼愉悅感提高了,整個項目代碼閱讀起來更加流暢,工作效率自然也會因此提高,同時代碼的健壯性也得到了保障。
環(huán)境說明
一般情況下,一個項目 應(yīng)該 有以下三個基本的項目環(huán)境:
Local - 開發(fā)環(huán)境
Staging - 線上測試環(huán)境
Production - 線上生產(chǎn)環(huán)境
關(guān)于多人開發(fā)
多人開發(fā),我們應(yīng)該保證各自計算機系統(tǒng)的統(tǒng)一,開發(fā)環(huán)境的統(tǒng)一,編碼工具的統(tǒng)一,只有各方面都是統(tǒng)一的,才有利于形成工作流和使用經(jīng)驗的傳承
代碼風格規(guī)范
代碼風格 必須 嚴格遵循 PSR-2 規(guī)范。
1.行文規(guī)范
- 問題分析
- 沒必要的注釋
- 沒對齊的 ->
- 沒有使用更簡潔的代碼
- bad code
//地址列表
public function fetchList(Request $request)
{
$data = DB::table('goldcat_address')
->whereNull('deleted_at')
->where([
'user_id' => $request->user_id,
])->get();
return success($data);
}
- good code
public function fetchList(Request $request)
{
$data = DB::table('goldcat_address')
->whereNull('deleted_at')
->where('user_id', $request->user_id)
->get();
return success($data);
}
2.重復(fù)的代碼片段,變量,以及沒必要的else
修改前 代碼字數(shù)統(tǒng)計2545 97行
<?php
/*
* @Author: your name
* @Date: 2021-06-29 13:45:05
* @LastEditTime: 2021-07-07 10:16:08
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \goldcat\app\Http\Controllers\Address\AddressController.php
*/
namespace App\Http\Controllers\Address;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class AddressController extends Controller
{
//地址列表
public function fetchList(Request $request)
{
$data = DB::table('goldcat_address')
->whereNull('deleted_at')
->where([
'user_id' => $request->user_id,
])->get();
return success($data);
}
//添加地址
public function add(Request $request)
{
$data = $request->all();
if ($request->default == 1) {
DB::table('goldcat_address')->where([
'user_id' => $request->header('uid'),
])->update([
'default' => 0,
]);
}
$id = DB::table('goldcat_address')->insertGetId($data);
return $this->fetchOne($id);
}
public function fetchOne($id)
{
$data = DB::table('goldcat_address')->find($id);
return success($data);
}
//更新
public function update(Request $request)
{
$data = DB::table('goldcat_address')->find($request->id);
if (isset($data->user_id) && $request->header('uid') == $data->user_id) {
$addressData = $request->all();
if ($request->default == 1) {
DB::table('goldcat_address')->where([
'user_id' => $request->header('uid'),
])->update([
'default' => 0,
]);
}
unset($addressData['user_id']);
unset($addressData['id']);
DB::table('goldcat_address')->where([
'id' => $request->id,
])
->update($addressData);
return $this->fetchOne($request->id);
} else {
return fail('授權(quán)失敗');
}
}
//刪除地址
public function del(Request $request)
{
DB::table('goldcat_address')
->where([
'id' => $request->id,
])
->update([
'deleted_at' => date('Y-m-d H:i:s'),
]);
return success('刪除成功');
}
}
修改后 代碼字數(shù)統(tǒng)計2157 80行
<?php
namespace App\Http\Controllers\Address;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class AddressController extends Controller
{
public function add(Request $request)
{
$data = $request->all();
if (1 == $request->default) {
DB::table('goldcat_address')
->where('user_id', $request->header('uid'))
->update(['default' => 0]);
}
$id = DB::table('goldcat_address')->insertGetId($data);
return $this->fetchOne($id);
}
public function update(Request $request)
{
$addressId = $request->id;
$authorId = DB::table('goldcat_address')
->where('id', $addressId)
->value('user_id');
if ($authorId && $request->header('uid') == $authorId) {
$addressData = $request->all();
if ($request->default == 1) {
DB::table('goldcat_address')
->where('user_id', $authorId)
->update(['default' => 0]);
}
unset($addressData['user_id']);
unset($addressData['id']);
DB::table('goldcat_address')
->where('id', $addressId)
->update($addressData);
return $this->fetchOne($addressId);
}
return fail('授權(quán)失敗');
}
public function fetchOne($id)
{
$data = DB::table('goldcat_address')->find($id);
return success($data);
}
public function fetchList(Request $request)
{
$data = DB::table('goldcat_address')
->whereNull('deleted_at')
->where('user_id', $request->user_id)
->get();
return success($data);
}
public function del(Request $request)
{
DB::table('goldcat_address')
->where('id', $request->id)
->update(['deleted_at' => date('Y-m-d H:i:s')]);
return success('刪除成功');
}
}
數(shù)據(jù)查詢時的一些格式規(guī)范
- 注意WHERE條件為1個和多個時的格式統(tǒng)一
- 使用新函數(shù)updateOrInsert 來代替先驗證是否存在,再根據(jù)結(jié)果進行新增或者更新的操作,以使代碼閱讀起來邏輯性更強
//銀行卡的添加
public function cardAdd(Request $request)
{
//查看該用戶的銀行卡數(shù)量是否超過3張,超過則不能繼續(xù)添加
$count = DB::table('goldcat_bank_card')
->where([
'uid' => $request->header('uid'),
'status' => 1,
])
->count();
if ($count >= 3) {
return fail('最多只能添加三張銀行卡');
}
$card = $request->card;
//對銀行卡號進行校驗,得到基本信息
$info = BankCard::info($card);
$validated = $info['validated'];
if (!$validated) {
return fail('卡號有誤');
}
//查看該銀行卡是否已經(jīng)存在
$realName = DB::table('goldcat_user_certification')
->where([
'uid' => $request->header('uid'),
'status' => 1,
])
->value('name');
$idCard = DB::table('goldcat_user_certification')
->where([
'uid' => $request->header('uid'),
'status' => 1,
])
->value('identity_card_number');
$exists = DB::table('goldcat_bank_card')->where([
'card' => $card,
'status' => 1,
])
->exists();
if ($exists) {
return fail('該銀行卡已被綁定');
}
//對銀行卡進行真實性檢驗:正式服調(diào)用實名信息,是否與申請人的姓名與身份證號一致
$check = $this->juheCheck($card, $realName, $idCard);
if (!$check) {
return fail('認證信息不匹配,銀行卡無效');
}
$name = $info['bankName'];
$cardTypeName = $info['cardTypeName'];
$bank = $info['bank'];
$exists = DB::table('goldcat_bank_card')->where([
'card' => $card,
])
->exists();
if ($exists) {
DB::table('goldcat_bank_card')
->where([
'card' => $card,
])
->update([
'uid' => $request->header('uid'),
'card' => $card, //卡號
'type' => $cardTypeName, //卡片類型
'bank' => $bank,
'name' => $name, //銀行名稱
'status' => 1,
]);
} else {
DB::table('goldcat_bank_card')
->insert([
'uid' => $request->header('uid'),
'card' => $card, //卡號
'type' => $cardTypeName, //卡片類型
'bank' => $bank,
'name' => $name, //銀行名稱
'status' => 1,
]);
}
return success('添加成功');
}
修改之后的
//銀行卡的添加
public function cardAdd(Request $request)
{
$card = $request->card;
//查看該用戶的銀行卡數(shù)量是否超過3張,超過則不能繼續(xù)添加
$count = DB::table('goldcat_bank_card')->where([
['uid', '=', $request->header('uid')],
['status', '=', 1],
])->count();
if ($count > 2) {
return fail('最多只能添加三張銀行卡');
}
//對銀行卡號進行校驗,得到基本信息
$info = BankCard::info($card);
if (!isset($info['validated']) || !$info['validated']) {
return fail('卡號有誤');
}
//查看該銀行卡是否已經(jīng)存在
$exists = DB::table('goldcat_bank_card')->where([
['status', '=', 1],
['card', '=', $card],
])->exists();
if ($exists) {
return fail('該銀行卡已被綁定');
}
//對銀行卡進行真實性檢驗:正式服調(diào)用實名信息,是否與申請人的姓名與身份證號一致
$userInfo = DB::table('goldcat_user_certification')->where([
['uid', '=', $request->header('uid')],
['status', '=', 1],
])->select('name'.'identity_card_number')->first();
if (!$userInfo) {
return fail('請先完成實名認證');
}
$check = $this->juheCheck($card, $userInfo->name, $userInfo->identity_card_number);
if (!$check) {
return fail('認證信息不匹配,銀行卡無效');
}
$bank = $info['bank'];
$name = $info['bankName'];
$cardTypeName = $info['cardTypeName'];
DB::table('goldcat_bank_card')->updateOrInsert([
'card' => $card, //卡號
'uid' => $request->header('uid'),
'type' => $cardTypeName, //卡片類型
'bank' => $bank,
'name' => $name, //銀行名稱
'status' => 1,
]);
return success('添加成功');
}
一段有問題的代碼
//根據(jù)銀行卡號返回銀行卡的銀行信息(銀行卡位數(shù)為16.17.19)
//預(yù)顯示 輸入第六位時就開始嘗試解析
public function backInfo(Request $request)
{
$card = $request->card;
//對于一個六位數(shù)至十位數(shù)的數(shù)字,對其進行后補零操作,使其分別變成三個數(shù)字,分別是16位,17位,19位
$cardLen = strlen($card);
$zeroNum = 16 - $cardLen;
$zero = '';
for ($i = 0; $i < $zeroNum; ++$i) {
$zero .= '0';
}
$card = $card.$zero;
for ($i = 17; $i < 20; ++$i) {
$card .= '0';
$info = BankCard::info($card);
$validated = $info['validated'];
if ($validated) {
return success([
'backName' => $info['bankName'],
'cardTypeName' => $info['cardTypeName'],
]);
}
}
}
問題分析
1.錯誤時會沒有返回值
2.邏輯有點混亂,讓人有些看不到
3.對于大于16位的數(shù)字無法處理
4.漏掉了對16位的數(shù)字的驗證修復(fù)后的代碼
//根據(jù)銀行卡號返回銀行卡的銀行信息(銀行卡位數(shù)為16.17.18 19)
//預(yù)顯示 輸入第六位時就開始嘗試解析
public function backInfo(Request $request)
{
$card = $request->card;
//對于一個小于20位的數(shù)字,補0使其位數(shù)逐漸增大,在此過程中匹配信息
for ($i = strlen($card); $i < 20; ++$i) {
$info = BankCard::info($card);
if ($card > 15) {
$validated = $info['validated'];
if ($validated) {
return success([
'backName' => $info['bankName'],
'cardTypeName' => $info['cardTypeName'],
]);
}
}
$card .= '0';
}
return fail('無法自動判斷');
}
一段丑陋的代碼會使人失去閱讀的欲望
public function rewardRead(Request $request)
{
$popreward20 = DB::table('goldcat_message')
->where(
[
'uid' => $request->header('uid'),
'status' => 0,
'type' => 4,
'user_type' => 1,
]
)
->value('type');
//注冊獎勵20的彈窗
if ($popreward20) {
DB::table('goldcat_message')
->where(
[
'uid' => $request->header('uid'),
'type' => 4,
]
)
->update(
[
'status' => 1,
]
);
return success(
[
'img' => 'pop-reward-20.png',
]
);
}
//認證獎勵30彈窗
$popreward30 = DB::table('goldcat_message')
->where(
[
'uid' => $request->header('uid'),
'status' => 0,
'type' => 5,
'user_type' => 1,
]
)
->value('type');
if ($popreward30) {
DB::table('goldcat_message')
->where(
[
'uid' => $request->header('uid'),
'type' => 5,
]
)
->update([
'status' => 1,
]
);
return success(
[
'img' => 'pop-reward-30.png',
]
);
}
return fail('無需彈窗提醒');
}
修復(fù)后
//認證成功獎勵到賬彈窗提示
public function rewardRead(Request $request)
{
$popreward20 = DB::table('goldcat_message')->where([
'uid' => $request->header('uid'),
'status' => 0,
'type' => 4,
'user_type' => 1,
])->value('type');
//注冊獎勵20的彈窗
if ($popreward20) {
DB::table('goldcat_message')->where([
'uid' => $request->header('uid'),
'type' => 4,
])->update(['status' => 1]);
return success(['img' => 'pop-reward-20.png']);
}
//認證獎勵30彈窗
$popreward30 = DB::table('goldcat_message')->where([
'uid' => $request->header('uid'),
'status' => 0,
'type' => 5,
'user_type' => 1,
])->value('type');
if ($popreward30) {
DB::table('goldcat_message')->where([
'uid' => $request->header('uid'),
'type' => 5,
])->update(['status' => 1]);
return success(['img' => 'pop-reward-30.png']);
}
return fail('無需彈窗提醒');
}
- 問題 閱讀代碼發(fā)現(xiàn)是要找出type為4或者為5的,并返回對應(yīng)的圖片和修改所返回的記錄的狀態(tài)為1
//認證成功獎勵到賬彈窗提示
public function rewardRead(Request $request)
{
$uid = 7; //$request->header('uid')
$res = DB::table('goldcat_message')->where([
'uid' => $uid,
'status' => 0,
'user_type' => 1,
])->where(function ($query) {
$query->where('type', '=', 4)
->orWhere('type', '=', 5);
})->select('id', 'type')->get();
$res = $res->toArray();
if (!$res) {
return fail('無需彈窗提醒');
}
$type = $res[0]->type;
DB::table('goldcat_message')->where([
'uid' => $uid,
'type' => $type,
])->update(['status' => 1]);
//注冊獎勵20或3認證獎勵30的彈窗
return $type == 4 ? success(['img' => 'pop-reward-20.png']) : success(['img' => 'pop-reward-30.png']);
}
代碼由68行變?yōu)榱?6行,代碼數(shù)也由2081變?yōu)榱?40,縮減了約60%,讀起來也更加的邏輯清晰了。