读码及代码优化封装技巧
读码
阅读代码其实就跟我们阅读文章一样,你首先要了解词义、语法,最好还知道这段文字的语境,这样你才能完整理解作者想表达的意图。同样的,读代码,你需要了解该编程语言的关键字、数据结构、语法以及代码块中变量的含义和环境(上下文)。相信写过代码的人都知道,相对于写码,阅读别人的代码(自己上周写的代码已成为自己眼中别人的代码)是一件痛苦的事情,日常读码中,一般是有以下两种场景:
- 学习上,比如在全球最大的同性交友网站github上阅读开源项目,讲道理这种场景下,我们一般选择的都是知名的开源项目,这类项目一般都有严格的代码审查流程并遵守业内的统一标准,符合我们的口味。可以看下hyperf 项目,hyperf的绝大部分组件都是基于PSR规范进行开发的,组件之间没有直接关联。比如其内置的容器组件,依据开发实践标准实现的,我们吃起来才合胃口。
- 工作上,基于业务开发的需要阅读相应的代码,目前我们团队使用 php-cs-fixer 在上git之前统一格式和 phpmd 代码质量检测工具,基本统一了代码风格,读起来还是相对友好,但毕竟是业务代码,因此除理解代码与注释外,还可以多走亿步来沟通清楚。
代码优化封装
当代码逻辑中有多个可单独拆分的功能时,就应该将其分离出来,倘若放任不管则后面难以维护,后期业务逻辑又有改动时,自己回头来看可能都很费工夫,长此以往,整个模块的复杂度显著提升,一个小小的改动可能就触动整个功能的稳定性,因此优化代码封装是必要的。
写码建议:
- 一个类文件不要超过1000行代码,超过1000行时很明显就要考虑拆分了,这样可以减少类的加载时间、内存频繁占用和回收,利于维护,对读码也友好
- 一个类只负责一项职责,遵守一个函数只做一件事的原则,这样做可以明显降低复杂度及提高可读性
- 函数的编写要尽量简洁,有复杂的条件嵌套判断时考虑逻辑分层,拆分为多个函数或进行抽象分离;优先考虑简单的条件,不符合就直接return出场或抛出异常。
- 遵循对应编程语言的编码标准,利于读码及维护
- 动手敲码前多些思考,如何进行逻辑拆解或抽象,上来就干只能是一时爽,前期的快速开发后面也将花费更多的时间精力来弥补
- 多读好的开源代码,从别人的实践中获取经验再反哺自己,有利于开阔写码的思路
- 设计模式作为最佳实践应该要好好掌握,在合适的场景使用合适的设计模式会让你的代码更加健壮
来个例子:
商城的售后目前有仅退款、退货退款、换货、整单取消这四个类型,下面我们来看看如何更好的实现生成售后单的功能。
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]));
}
}
以上只是最简单的业务逻辑,还没有完全拓展开来,实际中还应该继续拆分出入库、支付相关、邮件及日志等,优化代码的好处多多,这会使我们写出维护性更高、质量更好的代码,希望有朝一日我也能领会到写代码的哲学与快乐,加油,干饭人。