程序猿很多时候费了九牛二虎之力使用各种黑科技实现了某个功能,终于可以交差,但整个过程就像个噩梦,一般人是不太愿意回过头去阅读自己写的代码的,交出去的代码就让它如往事般随风吧。
可你不愿读自己的代码,却避免不了去读别人的代码,正如别人不愿读你的代码一样。因果循环,相互纠缠,出来混,迟早要还的。
下面是我从一款开源CMS系统中摘出来的代码,开源出来是很好的事情,在此仅从代码的角度来讨论一下。
<?php
function page($filename, $type = false)
{
global $_CMS, $_GP;
$do = '';
if ($type == true) {
$do = $_GP['do'] . "/";
}
if (SYSTEM_ACT == 'mobile') {
$source = SYSTEM_ROOT . $_CMS['module'] . "/template/mobile/" . $do . "{$filename}.php";
if (!is_file($source)) {
$source = SYSTEM_ROOT . "common/template/mobile/" . $do . "{$filename}.php";
}
} else {
$source = SYSTEM_ROOT . $_CMS['module'] . "/template/web/" . $do . "{$filename}.php";
if (!is_file($source)) {
$source = SYSTEM_ROOT . "common/template/web/" . $do . "{$filename}.php";
}
}
return $source;
}
写代码我一般要求格式,这种格式也算是一种套路,按套路来反复练习可以快速积累好的经验。
套路一:三段式;
1.初始化输入;
一般占代码行数20%左右
2.逻辑处理与运算
异常逻辑处理;
一般占代码行数40%~60%左右
正常逻辑处理;
一般占代码行数 20%~40%左右
3.返回
套路二:将逻辑嵌套转换为顺序执行( Guard Clauses);
逻辑一般通过if else或者switch这样的语句来呈现,提炼逻辑时,一般是大逻辑套小逻辑、小逻辑套小小逻辑、层层嵌套进去,这在没有任何封装的环境是正常的。
而一旦我们将代码放到一个函数或方法里面,事情就开始有所不同,因为有了return:每个函数我们会定义好它的返回值类型,return 可以让程序在走到某个逻辑分支时直接终止该函数的执行并返回结果;终止执行意味着就算没有if else嵌套,return 语句之后的代码也不会再执行。
利用这样的特性,我们就可以将嵌套尽量减少,使代码最终看起来是顺序执行的。
要达到顺序执行的目的,还有个先决条件:要学会将该函数和方法的执行过程和目标用一句描述出来,可以用中文,最好先注释在函数或方法体前面,比如:根据用户请求的模块、动作构造相应的视图文件绝对路径。
套路三:将逻辑提炼成数据
示例代码中一眼望去,代码重复度最高的是拼接完整文件路径的那部分,仔细看了以后,就能发现这是一段有规律的字符串:系统所处根目录+模块目录+控制器目录+动作名+后缀名,所不同的仅仅是模块名和控制器名,那么我们转换一下目标,玩个字符串填充的游戏,利用PHP的vsprintf函数就可简单实现。
示例中还有个逻辑,模块内找不到文件就降级去通用模块里找,这可算是框架的一种特性,但在这里不是我们关注的重点,重点是定义一个中间变量,就可以重用代码。
Linus在谈Git时表示:
Git的设计其实很简单,它有一个稳定而合理的数据结构。事实上,我强烈建议围绕着数据来设计代码,而不是反其道而行之,我觉得这可能就是 Git 如此成功的原因。坏程序员总是担心他们的代码,而优秀的程序员则会担心数据结构和它们之间的关系。
还有人说过,可执行程序文件本身就是一种数据。
这里的这个例子是否与Linus的理论匹配,我不太确定,但是想表达这样的一个意思。
调整过的最终代码:
<?php
function page(string $filename, bool $type = false): string
{
global $_CMS, $_GP;
$app = (SYSTEM_ACT == 'mobile') ? 'mobile' : 'web';
$do = ($type == true) ? $_GP['do'] . '/' : '';
$vf = SYSTEM_ROOT . "%s/template/{$app}/{$do}{$filename}.php";
$source = vsprintf($vf, [$_CMS['module']]);
if (!is_file($source)) {
$source = vsprintf($vf, ['common']);
}
return $source;
}