麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 開發 > PHP > 正文

給PHP開發者的編程指南 第一部分降低復雜程度

2024-05-04 23:42:19
字體:
來源:轉載
供稿:網友
給PHP開發者的編程指南,第一部分的內容是降低復雜程度,感興趣的小伙伴們可以參考一下
 

PHP 是一門自由度很高的編程語言。它是動態語言,對程序員有很大的寬容度。作為 PHP 程序員,要想讓你的代碼更有效,需要了解不少的規范。很多年來,我讀過很多編程方面的書籍,與很多資深程序員也討論過代碼風格的問題。具體哪條規則來自哪本書或者哪個人,我肯定不會都記得,但是本文(以及接下來的另一篇文章) 表達了我對于如何寫出更好的代碼的觀點:能經得起考驗的代碼,通常是非常易讀和易懂的。這樣的代碼,別人可以更輕松的查找問題,也可以更簡單的復用代碼。
降低函數體的復雜度

在方法或者函數體里,盡可能的降低復雜性。相對低一些的復雜性,可以便于別人閱讀代碼。另外,這樣做也可以減少代碼出問題的可能性,更易修改,有問題也更易修復。
在函數里減少括號數量

盡可能少的使用 if, elseif, else 和 switch 這些語句。它們會增加更多的括號。這會讓代碼更難懂、更難測試一些(因為每個括號都需要有測試用例覆蓋到)。總是有辦法來避免這個問題的。
代理決策 ("命令,不用去查詢(Tell, don't ask)")
有的時候 if 語句可以移到另一個對象里,這樣會更清晰些。例如:

 if($a->somethingIsTrue()) {  $a->doSomething(); }

可以改成:
                    $a->doSomething();
這里,具體的判斷由 $a 對象的 doSomething() 方法去做了。我們不需要再為此做更多的考慮,只需要安全的調用 doSomething() 即可。這種方式優雅的遵循了命令,不要去查詢原則。我建議你深入了解一下這個原則,當你向一個對象查詢信息并且根據這些信息做判斷的時候都可以適用這條原則。
使用map

有時可以用 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])) {  thrownew/LogicException(    '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" 的情況。這個在后面的文章會提到。
Return early

很多時候,函數里的一個分支并非真正的分支,而是前置或者后置的一些條件,就像這樣:// 前置條件

if(!$a instanceof A) {  throw new /InvalidArgumentException(...);} // happy pathreturn $a->someInformation();

這里 if 語句并不是函數執行的一個分支,它只是對一個前置條件的檢查。有時我們可以讓 PHP 自身來完成前置條件的檢查(例如使用恰當的類型提示)。不過,PHP 也沒法完成所有前置條件的檢查,所以還是需要在代碼里保留一些。為了降低復雜度,我們需要在提前知道代碼會出錯時、輸入錯誤時、已經知道結果時盡早返回。
盡早返回的效果就是后面的代碼沒必要像之前那樣縮進了:

// check preconditionif(...) {  thrownew...();} // return earlyif(...) {  return...;} // happy path... return...;

像上面這個模板這樣,代碼會變動更易讀和易懂。
創建小的邏輯單元

如果函數體過長,就很難理解這個函數到底在干什么。跟蹤變量的使用、變量類型、變量聲明周期、調用的輔助函數等等,這些都會消耗很多腦細胞。如果函數比較小,對于理解函數功能很有幫助(例如,函數只是接受一些輸入,做一些處理,再返回結果)。
使用輔助函數
在使用之前的原則減少括號之后,你還可以通過把函數拆分成更小的邏輯單元做到讓函數更清晰。你可以把實現一個子任務的代碼行看做一組代碼,這些代碼組直接用空行來分隔。然后考慮如何把它們拆分成輔助方法(即重構中的提煉方法)。
輔助方法一般是 private 的方法,只會被所屬的特定類的對象調用。通常它們不需要訪問實例的變量,這種情況需要定義為 static 的方法。在我的經驗中,private (static)的輔助方法通常會匯總到分離的類中,并且定義成 public (static 或 instance)的方法,至少在測試驅動開發的時候使用一個協作類就是這種情形。
減少臨時變量
長的函數通常需要一些變量來保存中間結果。這些臨時變量跟蹤起來比較麻煩:你需要記住它們是否已經初始化了,是否還有用,現在的值又是多少等等。
上節提到的輔助函數有助于減少臨時變量:

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();

這樣做可以簡化函數的組合。
雖然減少臨時變量通常會帶來好的設計,不過上面的例子中也沒必要干掉所有的臨時變量。有時候臨時變量的用處是很清晰的,作用也是一目了然的,就沒必要精簡。

使用簡單的類型

    追蹤變量的當前取值總是很麻煩的,當不清楚變量的類型時尤其如此。而如果一個變量的類型不是固定的,那簡直就是噩夢。
數組只包含同一種類型的值
    使用數組作為可遍歷的容器時,不管什么情況都要確保只使用同一種類型的值。這可以降低遍歷數組讀取數據的循環的復雜度:

foreach($collection as $value) {  // 如果指定$value的類型,就不需要做類型檢查}

你的代碼編輯器也會為你提供數組值的類型提示:

/** * @param DateTime[] $collection */public function doSomething(array $collection) {  foreach($collection as $value) {    // $value是DateTime類型  }}

而如果你不能確定 $value 是 DateTime 類型的話,你就不得不在函數里添加前置判斷來檢查其類型。beberlei/assert庫可以讓這個事情簡單一些:

useAssert/Assertion 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",然后程序崩潰。
下一篇文章我們會考慮 null,告訴你如何去處理它們。
可讀的表達式

我們已經討論過不少降低函數的整體復雜度的方法。在更細粒度上我們也可以做一些事情來減少代碼的復雜度。
隱藏復雜的邏輯

通常可以把復雜的表達式變成輔助函數。看看下面的代碼:

if(($a||$b) &&$c) {  ...}

可以變得更簡單一些,像這樣:

if(somethingIsTheCase($a,$b,$c)) {  ...}

閱讀代碼時可以清楚的知道這個判斷依賴 $a, $b 和 $c 三個變量,而函數名也可以很好的表達判斷條件的內容。
使用布爾表達式
if 表達式的內容可以轉換成布爾表達式。不過 PHP 也沒有強制你必須提供 boolean 值:

$a=new/DateTime();... if($a) {  ...}

$a 會自動轉換成 boolean 類型。強制類型轉換是 bug 的主要來源之一,不過還有一個問題是會對代碼的理解帶來復雜性,因為這里的類型轉換是隱式的。PHP 的隱式轉換的替代方案是顯式的進行類型轉換,例如:

if($a instanceof DateTime) {  ...}

如果你知道比較的是 bool 類型,就可以簡化成這樣:

if($b=== false) {  ...}

使用 ! 操作符則還可以簡化:

if(!$b) {  ...}

不要 Yoda 風格的表達式
Yoda 風格的表達式就像這樣:

if('hello'===$result) {  ...}

這種表達式主要是為了避免下面的錯誤:

if($result='hello') {  ...}

這里 'hello' 會賦值給 $result,然后成為整個表達式的值。'hello' 會自動轉換成 bool 類型,這里會轉換成 true。于是 if 分支里的代碼在這里會總是被執行。
使用 Yoda 風格的表達式可以幫你避免這類問題:

if('hello'=$result) {  ...}

我覺得實際情況下不太會有人出現這種錯誤,除非他還在學習 PHP 的基本語法。而且,Yoda 風格的代碼也有不小的代價:可讀性。這樣的表達式不太易讀,也不太容易懂,因為這不符合自然語言的習慣。

以上就是本文的全部內容,希望對大家的學習有所幫助。



注:相關教程知識閱讀請移步到PHP教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 韩国精品视频在线观看 | 亚洲第一色婷婷 | 港台三级在线观看 | 国产一级免费不卡 | 免费a级观看 | 日韩精品久久久久久 | 萌白酱福利视频在线网站 | 毛片天天看 | 精品国产一区二区久久 | 国产精品久久久久久婷婷天堂 | 精品亚洲一区二区三区 | 国产精品一区在线观看 | 最新久久免费视频 | 亚洲特黄a级毛片在线播放 久久久入口 | 国产91亚洲精品一区二区三区 | 福利在线小视频 | 高清做爰免费无遮网站挡 | 在线成人www免费观看视频 | 九一免费版在线观看 | 成人毛片视频免费看 | 欧美黑人伦理 | 欧美成年性h版影视中文字幕 | 国产精品一品二区三区四区18 | 在线成人www免费观看视频 | 黄色毛片免费视频 | 欧美性猛交xxx乱大交3蜜桃 | 91美女视频在线观看 | 92看片淫黄大片欧美看国产片 | 成人做爰高潮片免费视频美国 | 精国产品一区二区三区 | 在线看日本| 国产成年人小视频 | 99精品视频在线看 | 男女无套免费视频 | 成人午夜精品 | 黄色片网站免费在线观看 | 国产精品久久久久久久四虎电影 | 欧美黑人一级 | 久久色在线 | 操操操日日日干干干 | xxxx18韩国护士hd老师 |