譯者:白玉堂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
。