2024年3月

读码

阅读代码其实就跟我们阅读文章一样,你首先要了解词义、语法,最好还知道这段文字的语境,这样你才能完整理解作者想表达的意图。同样的,读代码,你需要了解该编程语言的关键字、数据结构、语法以及代码块中变量的含义和环境(上下文)。相信写过代码的人都知道,相对于写码,阅读别人的代码(自己上周写的代码已成为自己眼中别人的代码)是一件痛苦的事情,日常读码中,一般是有以下两种场景:

  1. 学习上,比如在全球最大的同性交友网站github上阅读开源项目,讲道理这种场景下,我们一般选择的都是知名的开源项目,这类项目一般都有严格的代码审查流程并遵守业内的统一标准,符合我们的口味。可以看下hyperf 项目,hyperf的绝大部分组件都是基于PSR规范进行开发的,组件之间没有直接关联。比如其内置的容器组件,依据开发实践标准实现的,我们吃起来才合胃口。
  2. 工作上,基于业务开发的需要阅读相应的代码,目前我们团队使用 php-cs-fixer 在上git之前统一格式和 phpmd 代码质量检测工具,基本统一了代码风格,读起来还是相对友好,但毕竟是业务代码,因此除理解代码与注释外,还可以多走亿步来沟通清楚。

代码优化封装

当代码逻辑中有多个可单独拆分的功能时,就应该将其分离出来,倘若放任不管则后面难以维护,后期业务逻辑又有改动时,自己回头来看可能都很费工夫,长此以往,整个模块的复杂度显著提升,一个小小的改动可能就触动整个功能的稳定性,因此优化代码封装是必要的。

写码建议:

  1. 一个类文件不要超过1000行代码,超过1000行时很明显就要考虑拆分了,这样可以减少类的加载时间、内存频繁占用和回收,利于维护,对读码也友好
  2. 一个类只负责一项职责,遵守一个函数只做一件事的原则,这样做可以明显降低复杂度及提高可读性
  3. 函数的编写要尽量简洁,有复杂的条件嵌套判断时考虑逻辑分层,拆分为多个函数或进行抽象分离;优先考虑简单的条件,不符合就直接return出场或抛出异常。
  4. 遵循对应编程语言的编码标准,利于读码及维护
  5. 动手敲码前多些思考,如何进行逻辑拆解或抽象,上来就干只能是一时爽,前期的快速开发后面也将花费更多的时间精力来弥补
  6. 多读好的开源代码,从别人的实践中获取经验再反哺自己,有利于开阔写码的思路
  7. 设计模式作为最佳实践应该要好好掌握,在合适的场景使用合适的设计模式会让你的代码更加健壮

来个例子:

商城的售后目前有仅退款、退货退款、换货、整单取消这四个类型,下面我们来看看如何更好的实现生成售后单的功能。

1.采用拆解函数的方式,每种售后方式为一个函数

<?php declare(strict_types=1);

namespace Order;

class AfterSaleOrder {
    const SALES_TYPE = [
        'only_refund'             => 1, //仅退款
        'return_goods_and_refund' => 2, //退货退款
        'exchange_goods'          => 3, //换货
        'order_cancel'            => 4, //整单取消
    ];

    private static $refundData;

    /**
     * 初始化数据
     * @param array $refundData
     * @return $this|null
     */
    public function setAfterData (array $refundData) {
        self::$refundData = $refundData;
        if (self::checkRefundData()) {
            return $this;
        }

        return null;
        //todo
    }

    /**
     * 验证退款参数
     * @return bool
     */
    private static function checkRefundData () :bool{
        if (!isset(self::$refundData['sales_type']) || !in_array(self::$refundData['sales_type'], self::SALES_TYPE)) {
            return false;
        }
        //todo
        return true;
    }

    public static function execute() :bool{
        $res = self::create();
        if ($res) {
            var_dump("successful.");
            return true;
            //todo
        }
        //todo
        return false;
    }

    /**
     * 仅退款
     * @return bool
     */
    private static function onlyRefund() :bool{
        print_r(self::$refundData);
        var_dump("only_refund successful.");
        //todo
        return true;
    }

    /**
     * 退货退款
     * @return bool
     */
    private static function returnGoodsAndRefund() :bool{
        print_r(self::$refundData);
        var_dump("return_goods_and_refund successful.");
        //todo
        return true;
    }

    /**
     * 换货
     * @return bool
     */
    private static function exchangeGoods() :bool{
        print_r(self::$refundData);
        var_dump("exchange_goods successful.");
        //todo
        return true;
    }

    /**
     * 整单取消
     * @return bool
     */
    private static function orderCancel() :bool{
        print_r(self::$refundData);
        var_dump("order_cancel successful.");
        //todo
        return true;
    }

    /**
     * 处理退款
     * @return bool
     */
    private static function create() :bool{
        if (self::SALES_TYPE['only_refund'] == self::$refundData['sales_type']) {
            return self::onlyRefund();
        }

        if (self::SALES_TYPE['return_goods_and_refund'] == self::$refundData['sales_type']) {
            return self::returnGoodsAndRefund();
        }

        if (self::SALES_TYPE['exchange_goods'] == self::$refundData['sales_type']) {
            return self::exchangeGoods();
        }

        if (self::SALES_TYPE['order_cancel'] == self::$refundData['sales_type']) {
            return self::orderCancel();
        }

        return false;
    }
}

2.用抽象的方式拆解,每种售后方式为一个类,且都继承售后基类用来约束一些必须实现的功能

业务调用类
<?php

namespace Order\Obj;

use Order\Obj\AfterSaleModel\AfterSaleAbstract;
use Order\Obj\AfterSaleModel\ExchangeGoods;
use Order\Obj\AfterSaleModel\OrderCancel;
use Order\Obj\AfterSaleModel\OnlyRefund;
use Order\Obj\AfterSaleModel\ReturnGoodsAndRefund;

class AfterSaleOrder {

    const SALES_TYPE = [
        'only_refund'             => 1, //仅退款
        'return_goods_and_refund' => 2, //退货退款
        'exchange_goods'          => 3, //换货
        'order_cancel'            => 4, //整单取消
    ];

    //退款参数
    private static $refundData;
    //退款模型
    private static $afterSaleObj;

    public function __construct (array $refundData) {
        if (self::checkRefundData($refundData)) {
            self::$refundData = $refundData;
            self::$afterSaleObj = self::getAfterSaleObj();
            return $this;
        }

        return null;
        //todo
    }

    /**
     * 验证退款参数
     * @param array $refundData
     * @return bool
     */
    private static function checkRefundData (array $refundData) :bool{
        if (!isset($refundData['sales_type']) || !in_array($refundData['sales_type'], self::SALES_TYPE)) {
            return false;
        }
        //todo
        return true;
    }

    public static function execute() :bool{
        $res = self::create();
        if ($res) {
            var_dump("successful.");
            return true;
            //todo
        }
        //todo
        return false;
    }

    /**
     * 获取退款模型
     * @return null|AfterSaleAbstract
     */
    private static function getAfterSaleObj() :?AfterSaleAbstract{
        if (self::SALES_TYPE['only_refund'] == self::$refundData['sales_type']) {
            return new OnlyRefund(self::$refundData);
        }

        if (self::SALES_TYPE['return_goods_and_refund'] == self::$refundData['sales_type']) {
            return new ReturnGoodsAndRefund(self::$refundData);
        }

        if (self::SALES_TYPE['exchange_goods'] == self::$refundData['sales_type']) {
            return new ExchangeGoods(self::$refundData);
        }

        if (self::SALES_TYPE['order_cancel'] == self::$refundData['sales_type']) {
            return new OrderCancel(self::$refundData);
        }

        return null;
    }

    /**
     * 操作退款
     * @return bool
     */
    private static function create() :bool{
        if (self::$afterSaleObj) {
            return self::$afterSaleObj::execute();
        }
        //todo
        return false;
    }
}
售后基类:用来约束实体类,必须实现execute函数
<?php

namespace Order\Obj\AfterSaleModel;

abstract class AfterSaleAbstract {

    private static $data;

    public function __construct (array $data) {
        self::$data = $data;
    }

    abstract static function execute () :bool;

}
实体类:仅退款、退货退款、换货、整单取消
<?php

namespace Order\Obj\AfterSaleModel;

class ReturnGoodsAndRefund extends AfterSaleAbstract {

    private static $data;

    public function __construct (array $data) {
        parent::__construct($data);
    }

    public static function execute () :bool{
        if (self::refund() && self::inStorage()) {
            return true;
        }
        //todo
        self::setLogData();
        return false;
    }

    private static function refund() :bool{
        var_dump('退款');
        return true;
    }

    private static function inStorage() :bool{
        var_dump('入库');
        return true;
    }

    private static function setLogData() {
        var_dump(json_encode(['type' => 'refund_goods_and_refund','data' => self::$data]));
    }
}

以上只是最简单的业务逻辑,还没有完全拓展开来,实际中还应该继续拆分出入库、支付相关、邮件及日志等,优化代码的好处多多,这会使我们写出维护性更高、质量更好的代码,希望有朝一日我也能领会到写代码的哲学与快乐,加油,干饭人。