给PHP开发者的编程指南 第一部分降低复杂程度
《:给PHP开发者的编程指南 第一部分降低复杂程度》要点: PHP 是一门自由度很高的编程语言.它是动态语言,对程序员有很大的宽容度.作为 PHP 程序员,要想让你的代码更有效,需要了解不少的规范.很多年来,我读过很多编程方面的书籍,与很多资深程序员也讨论过代码风格的问题.具体哪条规则来自哪本书或者哪个人,我肯定不会都记得,但是本文(以及接下来的另一篇文章) 表达了我对于如何写出更好的代码的观点:能经得起考验的代码,通常是非常易读和易懂的.这样的代码,别人可以更轻松的查找问题,也可以更简单的复用代码. 在办法或者函数体里,尽可能的降低复杂性.相对低一些的复杂性,可以便于别人阅读代码.另外,这样做也可以减少代码出问题的可能性,更易修改,有问题也更易修复. 尽可能少的使用 if,elseif,else 和 switch 这些语句.它们会增加更多的括号.这会让代码更难懂、更难测试一些(因为每个括号都需要有测试用例覆盖到).总是有方法来避免这个问题的. if($a->somethingIsTrue()) { $a->doSomething(); } 可以改成: 有时可以用 map 语句减少 if,elseif 或 else 的使用,例如: if($type==='json') { return $jsonDecoder->decode($body); }elseif($type==='xml') { return $xmlDecoder->decode($body); }else{ throw new LogicException( 'Type "'.$type.'" is not supported' ); } 可以精简为: $decoders= ...;// a map of type (string) to corresponding Decoder objects if(!isset($decoders[$type])) { thrownewLogicException( 'Type "'.$type.'" is not supported' ); } 这样使用 map 的方式也让你的代码遵循扩展开放,关闭修改的原则. 很多 if 语句可以通过更严格的使用类型来避免,例如: if($a instanceof A) { // happy path return $a->someInformation(); }elseif($a=== null) { // alternative path return 'default information'; } 可以通过强制 $a 使用 A 类型来简化: return $a->someInformation(); 当然,我们可以通过其他方式来支持 "null" 的情况.这个在后面的文章会提到. 很多时候,函数里的一个分支并非真正的分支,而是前置或者后置的一些条件,就像这样:// 前置条件 if(!$a instanceof A) { throw new InvalidArgumentException(...); } // happy path return $a->someInformation(); 这里 if 语句并不是函数执行的一个分支,它只是对一个前置条件的检查.有时我们可以让 PHP 自身来完成前置条件的检查(例如使用恰当的类型提示).不过,PHP 也没法完成所有前置条件的检查,所以还是需要在代码里保留一些.为了降低复杂度,我们需要在提前知道代码会出错时、输入错误时、已经知道结果时尽早返回. // check precondition if(...) { thrownew...(); } // return early if(...) { return...; } // happy path ... return...; 像上面这个模板这样,代码会变动更易读和易懂. 如果函数体过长,就很难理解这个函数到底在干什么.跟踪变量的使用、变量类型、变量声明周期、调用的辅助函数等等,这些都会消耗很多脑细胞.如果函数比较小,对于理解函数功能很有帮助(例如,函数只是接受一些输入,做一些处理,再返回结果). public function capitalizeAndReverse(array $names) { $capitalized = array_map('ucfirst',$names); $capitalizedAndReversed = array_map('strrev',$capitalized); return $capitalizedAndReversed; } 使用辅助办法,我们可以不用临时变量了: public function capitalizeAndReverse(array $names) { return self::reverse( self::capitalize($names) ); } private static function reverse(array $names) { return array_map('strrev',$names); } private static function capitalize(array $names) { return array_map('ucfirst',$names); } 正如你所见,我们把函数变成新函数的组合,这样变得更易懂,也更容易修改.某种方式上,代码还有点符合“扩展开放/修改关闭”,因为我们基本上不需要再修改辅助函数. classNames { private $names; public function __construct(array $names) { $this->names = $names; } public function reverse() { return new self( array_map('strrev',$names) ); } public function capitalize() { return new self( array_map('ucfirst',$names) ); } } $result = (newNames([...]))->capitalize()->reverse(); 这样做可以简化函数的组合. 使用简单的类型PHP实例 ??? 追踪变量的当前取值总是很麻烦的,当不清楚变量的类型时尤其如此.而如果一个变量的类型不是固定的,那简直就是噩梦. foreach($collection as $value) { // 如果指定$value的类型,就不需要做类型检查 } 你的代码编辑器也会为你提供数组值的类型提示: /** * @param DateTime[] $collection */ public function doSomething(array $collection) { foreach($collection as $value) { // $value是DateTime类型 } } 而如果你不能确定 $value 是 DateTime 类型的话,你就不得不在函数里添加前置判断来检查其类型.beberlei/assert库可以让这个事情简单一些: useAssertAssertion public function doSomething(array $collection) { Assertion::allIsInstanceOf($collection,DateTime::class); ... } 如果容器里有内容不是 DateTime 类型,这会抛出一个 InvalidArgumentException 异常.除了强制输入相同类型的值之外,使用断言(assert)也是降低代码复杂度的一种手段,因为你可以不在函数的头部去做类型的检查. $result= someFunction(); if($result=== false) { ... }else if(is_int($result)) { ... } PHP 并不能阻止你返回不同类型的值(或者使用不同类型的参数).但是这样做只会造成大量的混乱,你的程序里也会到处都充斥着 if 语句. /** * @param int $id * @return User|null */ public function findById($id) { ... } 这个函数会返回 User 对象或者 null,这种做法是有问题的,如果不检查返回值是否合法的 User 对象,我们是不能去调用返回值的办法的.在 PHP 7之前,这样做会造成"Fatal error",然后程序崩溃. 我们已经讨论过不少降低函数的整体复杂度的方法.在更细粒度上我们也可以做一些事情来减少代码的复杂度. 通常可以把复杂的表达式变成辅助函数.看看下面的代码: if(($a||$b) &&$c) { ... } 可以变得更简单一些,像这样: if(somethingIsTheCase($a,$b,$c)) { ... } 阅读代码时可以清楚的知道这个判断依赖 $a,$b 和 $c 三个变量,而函数名也可以很好的表达判断条件的内容. $a=newDateTime(); ... if($a) { ... } $a 会自动转换成 boolean 类型.强制类型转换是 bug 的主要来源之一,不过还有一个问题是会对代码的理解带来复杂性,因为这里的类型转换是隐式的.PHP 的隐式转换的替代方案是显式的进行类型转换,例如: if($a instanceof DateTime) { ... } 如果你知道比较的是 bool 类型,就可以简化成这样: if($b=== false) { ... } 使用 ! 操作符则还可以简化: if(!$b) { ... } 不要 Yoda 风格的表达式 if('hello'===$result) { ... } 这种表达式主要是为了避免下面的错误: if($result='hello') { ... } 这里 'hello' 会赋值给 $result,然后成为整个表达式的值.'hello' 会自动转换成 bool 类型,这里会转换成 true.于是 if 分支里的代码在这里会总是被执行. if('hello'=$result) { ... } 我觉得实际情况下不太会有人出现这种错误,除非他还在学习 PHP 的基本语法.而且,Yoda 风格的代码也有不小的代价:可读性.这样的表达式不太易读,也不太容易懂,因为这不符合自然语言的习惯.PHP实例 以上就是本文的全部内容,希望对大家的学习有所赞助.PHP实例 《:给PHP开发者的编程指南 第一部分降低复杂程度》是否对您有启发,欢迎查看更多与《:给PHP开发者的编程指南 第一部分降低复杂程度》相关教程,学精学透。编程之家 52php.cn为您提供精彩教程。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |