译者:白玉堂php
做者:Jason McCreary程序员
我不相信写代码的硬性规则,可是你常常能听到。好比一个方法不该该超过15行,方法只应该有一个 return
语句,缩进必须是4个空格等等。这些规则太死板了。
实际代码要灵活的多。这些硬性规则带来的反作用是让咱们偏离了实质 可读性
。若是个人关注点彻底放在行数或 return
语句,这只会阻碍本身写高可读性代码,由于它们有很多都“太长”或多个 return
语句。
不少这些硬性规则试图解决嵌套代码问题。嵌套代码很难跟进逻辑去理解。感官上,这样更须要用眼睛扫描更多;心理上,每一个嵌套层级都须要付出更多精力跟踪理解功能,这些都会让阅读者费劲。
嵌套代码主要是条件判断的结果。自从条件语句成为编程逻辑的基础,咱们不能很好的移除它们。咱们必须识别出他们对读者的影响,采起措施减小这种影响。 编程
为了提升可读性,咱们想让代码回到顶部。循环和条件语句天生就有一个嵌套结构,在这些代码块中没法避免的要嵌套。然而,咱们能够避免在此结构以外的嵌套。
咱们一块儿看几个嵌套代码的例子来练习一下提升他们的可读性。 swift
可能你不相信我,可是我不止一次的看到这样的代码:api
<?php
public function handle($request, Closure $next) {
if (env('APP_ENV') == 'development') {
// do nothing...
} else {
if ($request->getScheme() != 'https') {
URL::forceScheme('https');
return redirect('https://www.example.com/' . $request->getPath());
}
}
return $next($request);
}
复制代码
就是这,一个空的 if
语句块,我还看到过另外一面:一个空的 else
代码块。没有规定一个 if
必须和一个 else
成对出现。至少在过去的 20 多年没有任何一门编程语言规定这样。空代码块是死代码,删除他们。 数组
嵌套代码常常会返回一个值。若是值是 boolean
型,这是压缩代码的机会,直接 return
掉条件判断。
考虑一下这个 Set
类中 isEmpty
方法的嵌套代码:安全
<?php
public function isEmpty() {
if ($this->size === 0) {
return true;
} else {
return false;
}
}
复制代码
尽管这个方法块只有 4 行代码,还包括多个子块。即便这么少的代码行数,也难于阅读,由于它让代码比它实际要出现更高的复杂度。
经过识别原始 boolean
值的条件返回,咱们有很好的机会经过直接返回条件彻底删除嵌套代码。数据结构
<?php
public function isEmpty() {
return $this->size === 0;
}
复制代码
结合这个恰当的方法命名和如今的一行代码块的上下文,咱们下降了代码的理解复杂度。尽管这行代码可能会比较密集,但它仍然比重构前更具可读性。
注:压缩条件判断能够适用于不少数据类型不只限于布尔类型。例如,你可使用简单类型的整型做为条件返回。而后,这也会迅速的增长了复杂度。不少的编程者尝试使用三目运算来解决这种状况。可是三目运算可以节省代码却没有下降复杂度,还会下降代码可读性。在这种状况下,卫语句是更好的选择。架构
嵌套代码常常是逻辑层次递进的结果。咱们做为编程者,要写出每个条件直到能够安全操做的程度。
可是这个流程对程序执行来讲是典范,对代码阅读却不是。由于每一层代码嵌套,代码阅读者必须保持一种持续增长的思惟模式。
细想下面的 Set
类 add
方法的实现:
<?php
public function add($item) {
if ($item !== null) {
if (!$this->contains($item)) {
$this->items[] = $item;
}
}
}
复制代码
代码逻辑是这样:若是item
不是 null
而且 若是 Set
类不包含 item
,就把 item 添加到数组里。问题是:这不只这么简单的操做让人感到复杂,更让主要操做埋藏在最深层的代码里。
理想状态下,代码的主要操做位于顶部。咱们能够把条件语句重构成卫语句,达到解开嵌套语句和突出主要操做的目的。
卫语句只是想保护咱们的方法不受一场路径的干扰。尽管他们一般初夏你在代码块的顶部,他们也能够出如今任何地方。咱们能够运用 De Morgan's Laws 把任何的嵌套条件转换成卫语句并放弃层层的控制。代码里,这意味着咱们不用条件语句,并采用 return
语句。
把以上思路应用到 add
方法,咱们的实现以下:
<?php
public function add($item) {
if ($item === null || $this->contains($item)) {
return;
}
$this->items[] = $item;
}
复制代码
译者注:其实卫语句也运用了《重构》里常常提到的尽早返回的思想,把异常状况直接打回去
这么作,咱们不只提炼了主逻辑,还突显出方法的异常路径。如今对将来的代码阅读者少了一些复杂度,这也让被突显出的异常状况更易于测试。
switch
语句是一个很是啰嗦的语法结构,switch
有固定的 4 个关键字和三个级别。即便代码块里只有几行代码仍然要阅读不少代码。虽然者在某些状况下能够接受,但在其余状况下并非。
有的状况使用 if
语句来替代 switch
语句可能会产生更多较高可读性代码。
switch 语句很是适用于 1 :1
的已有 case 语句而且有他们本身多行的代码块场景。不管这些代码行是赋值,return
语句或者方法调用,比率大体为 1 :1
则可读性几乎保持不变。
<?php
switch ($command) {
case 'action':
startRecording();
break;
case 'cut':
stopRecording();
break;
case 'lights':
adjustLighting();
break;
}
复制代码
注:当 switch 语句是流线型的状况下,不少编程者使用字典/数据表,或多态代替。全部这些倒是其余的替代品。就记住每个方案都有权衡(复杂性),对于大多数代码,switch 语句一般“足够好”。
另外一个嵌套的表现是循环,循环具备自然的复杂度。做为一个编程者,咱们就像被一我的诅咒了同样,总想...增量逻辑。一样,做为人类,咱们不是计算机,咱们不太可能赢得了和计算机的循环计算。计算机会一直比人类强大,能和复杂惟一与之抗衡的是可读性。
我不会介绍可能会改进你代码的数据结构或算法。那比较有特定性。一般,大多数循环处理累加或调用。若是你发现你的代码库包含太多循环,看下是否有相似 filter / map / reduce 等的高阶函数被使用。虽然这可能没法帮助全部的阅读代码者改进可读性,但它会提升你的我的技能。
我经常在想,咱们为何要和代码作斗争?
一、代码没有错,代码构建的软件帮助公司创造价值,争取市场份额,创造了收益。
二、软件随着功能和需求的迭代,原始的架构设计确定不能知足后续的变动,结果形成代码臃肿,维护成本增长,软件风险增长,也间接加重了产品的风险,甚至对公司营收都会形成风险和影响。
三、软件的腐烂不可避免,咱们能作的是延缓这种腐烂,不断重构咱们的代码,重构不必定是整个接口或功能所有推倒重来,也能够是一个函数的优化,一行代码的优化。这些小的动做都能下降代码腐烂的速度,下降bug数,下降维护成本和扩展成本。于公,能够为公司节省开支;与私,开发也能早点下班;于职业规划,好的代码也是一个程序员的门面,对本身的代码质量和编程习惯负责也是之后求职的一个竞争优点。
基于原做者的分享,我也分享我看到的,在代码编写初期就能改进的,(若是代码编写完成了,再去重构,代价会更大),这些小动做就和“勿以善小而不为”一个道理,聚沙成塔,咱们天天都能写出比昨天更好的代码。
<?php
class Do {
public $api_url = 'http://www.domain.com/api/name/action';
public function requestRemoteApi(array $params) {
$url = $this->api_url;
// ... do someting
$res = HttpHelper::post($url,$data);
return $res;
}
}
复制代码
好比这里有两点:
直接写
<?php
class Do {
public $api_url = 'http://www.domain.com/api/name/action';
public function requestRemoteApi(array $params) {
// ... do someting
return HttpHelper::post($this->api_url,$data);
}
}
复制代码
在代码优化的原则里的确是有一个“延迟决定”的思想,就是说当咱们作代码抽象的时候,一次是特例,两次是偶然,三次你就须要考虑抽象封装了。
可是在代码里咱们每每看到完彻底全的复制,只修改了一些简单的一行配置或者 变量名不一样,更有甚者惧于生产环境风险直接复制了一个新的文件,甚至一个包含一千多个文件的文件夹,命名后缀只是简单的加了数字1/2/3/4 等等 folderName1
, folderName2
, folderName3
。