前言
我经常能听到一些对话
狗腿子A:哇 我刚刚去改**项目的代码,看的我有点怀疑人生
狗腿子B: 我现在项目的跟屎山一样
狗腿子C: 我隔壁那哥们每天写代码都特别随性,我有点按耐不住我的刀
.....
今天跟大家聊聊一些 我眼中 好孩子的编码习惯,而不是代码风格习惯 (psr-*
),当然还是强烈建议大家代码风格跟psr-4
靠齐。
推荐一本《代码整洁之道》,这本书我已经书都快翻烂了,墙裂推荐!!!
不过度的if嵌套判断
案例代码
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
导致最上层的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多了,排查日志也是一件很痛苦的事情。
1.尽可能业务最上层包裹异常 除非网络IO请求函数。
2.如果非要异常嵌套 需要定义每个异常的类型。
3.尽可能根据特定的异常进行catch 不建议直接catch Exception。
4.异常和日志是个cp
,还是不要忘记了。
不要用if-else做错误类型判断
案例代码 (来源某个网民前段时间咨询)
这样的代码可能写起来特别舒服,但是后期进行业务的增加改写和时间的沉淀,容易变成让人害怕的屎山代码。
我们用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.根据错误码能直接快速定位到错误代码
建议
1050001,
"message" => "用户已被停用"
];
}
$this->error(UserErrorCode::USER_DISABLE_ERROR);
错误码建议
1-2位 - 项目码 | 3-4位 - 模块码 | 5-7位具体业务错误码
可靠的命名规范
不可靠的命名总会让人误导。
比如变量命名为userArrayList 我以为是个数组列表变量,事实上这个特么是个对象列表。
1.做有意义的区分
比如 singleUserItem
跟userItem
有啥区别
比如 getUserList
跟getUsers
有啥区别
2.可以通过搜索翻译
能知道的变量含义
不要把变量贴入搜索翻译
会出现七七八八的东西
3.如果真的不知道该怎么翻试试用拼音把别硬凹了
比如之前做百度
的一个接口对接
变量命名为hundredDegree
而不是baidu
其他的可以参照《代码简洁之道》
函数的单一职责
最最最最后也是最重要的,代码的恶心大多数来源于函数的职责不清晰,有全都塞在一起的、东一块西一块的。
其实关于单一职责有很多文章在描述,如何去检验或者去写符合标准的单一职责。
画流程图
如果你能把业务的流程图画的特别清晰,那么你的函数的职责也就定下来了。
最后
上述为洪光光
心中的好孩子的习惯,也有可能是你眼中坏孩子的习惯。如果你认为是坏孩子的习惯或者认为还有其他好孩子的习惯欢迎评论撕逼讨论。
毕竟