好孩子的编码习惯

前言

我常常能听到一些对话
狗腿子A:哇 我刚刚去改**项目的代码,看的我有点怀疑人生
狗腿子B: 我如今项目的跟屎山同样
狗腿子C: 我隔壁那哥们天天写代码都特别随性,我有点按耐不住个人刀
.....
image.png
今天跟你们聊聊一些 我眼中 好孩子的编码习惯,而不是代码风格习惯 (psr-*),固然仍是强烈建议你们代码风格跟psr-4靠齐。php

离职流程.png

推荐一本《代码整洁之道》,这本书我已经书都快翻烂了,墙裂推荐!!!

image.png

不过分的if嵌套判断

案例背景
有个函数须要判断用户是否参与活动
流程图1.png前端

案例代码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致使最上层的try-catch catch了寂寞。
image.png微信

案例代码网络

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,仍是不要忘记了。
image.png函数

<?php

function insertUser($data)
{
    try {
        userIsInValid();
    } catch (Exception $exception) {
        // 日志
        // 业务处理
    } catch (HttpException $httpException) {
       // 日志
       // 业务处理
    }
}

function userIsInValid()
{
    //
    return true;
}

不要用if-else作错误类型判断

案例代码 (来源某个网民前段时间咨询)

<?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', [], '该用户今日收款次数超过限制');
}

这样的代码可能写起来特别舒服,可是后期进行业务的增长改写和时间的沉淀,容易变成让人惧怕的屎山代码。

image.png

咱们用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);

在合适的场景使用设计模式

上述可能只能针对错误码进行改造,若是万一咱们须要不一样的错误进行逻辑处理还怎么办。这时候能够考虑用设计模式 (好比用以多态取代条件表达式)

设计模式固好但不要过分使用,否则整个项目更难维护,你要坚信将来的你队友不知道是什么样的生物

image.png

$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.作有意义的区分
好比 singleUserItemuserItem有啥区别
好比 getUserListgetUsers有啥区别
image.png

2.能够经过搜索翻译能知道的变量含义
不要把变量贴入搜索翻译会出现七七八八的东西
3.若是真的不知道该怎么翻试试用拼音把别硬凹了
好比以前作百度的一个接口对接
变量命名为hundredDegree而不是baidu
image.png
其余的能够参照《代码简洁之道》

函数的单一职责

最最最最后也是最重要的,代码的恶心大多数来源于函数的职责不清晰,有全都塞在一块儿的、东一块西一块的。
其实关于单一职责有不少文章在描述,如何去检验或者去写符合标准的单一职责。
画流程图
若是你能把业务的流程图画的特别清晰,那么你的函数的职责也就定下来了。
image.png

<?php

// 兑换逻辑
function doExchange()
{
    if (checkIsLock()) {
        
    }
    lock();
    if (!checkUserIsExchange()) {
        
    }
    costUserPoint();
    exchangeGoods();
}
// 判断是否悲观锁
function checkIsLock(){}
// 上悲观锁
function lock(){}
// 判断用户是否能够兑换
function checkUserIsExchange(){}
// 扣除积分
function costUserPoint(){}
// 兑换商品
function exchangeGoods(){}

最后

上述为洪光光心中的好孩子的习惯,也有多是你眼中坏孩子的习惯。若是你认为是坏孩子的习惯或者认为还有其余好孩子的习惯欢迎评论撕逼讨论。
毕竟
image.png

相关文章
相关标签/搜索