譯者:蔣宇捷css
一月兩次,咱們重溫Nettuts歷史上讀者最喜歡的文章。html
代碼可讀性是一個計算機編程世界的廣泛主題。它是咱們做爲開發者第一件學習的事情。這篇文章將闡述編寫可讀性代碼十五個最重要的最佳實踐。mysql
集成開發環境IDE在過去的短短几年裏走過了很長的路。它使得註釋代碼比之前更加有用。依照特定標準書寫的註釋容許IDE和其餘工具經過不一樣的方式來使用它們。sql
考慮以下示例:數據庫
我在函數定義中添加的註釋能夠在調用它的地方看到,即使是在其餘文件中。編程
這裏是我另一個從第三方庫中調用函數的例子:vim
在這些特殊的例子中,使用的註釋(或者文檔)類型基於PHPDoc,IDE是Aptana。api
我假定你已經知道了你必需要縮進你的代碼。然而,保持排版樣式一致仍然是一個好主意。框架
這裏有不止一種方式來進行代碼排版。
function foo() { if ($maybe) { do_it_now(); again(); } else { abort_mission(); } finalize(); }
function foo() { if ($maybe) { do_it_now(); again(); } else { abort_mission(); } finalize(); }
function foo() { if ($maybe) { do_it_now(); again(); } else { abort_mission(); } finalize(); }
我曾經使用第二種樣式可是最近換爲第一種。可是這僅僅只表明了一種偏心。這裏並無每一個人必需要遵照的「最好的」樣式。事實上,最佳的樣式,就是一致的樣式。若是你是一個小組的一部分或者你在爲一個項目貢獻代碼,你必須依照這個項目以前使用的樣式。
排版的樣式總不是徹底和另一個不一樣。有時,它們混合了多種不一樣的規則。例如,按照PEAR編碼標準,前括弧「{」和控制結構在同一行上,可是在功能定義後放在第二行上。
PEAR樣式:
function foo() { // placed on the next line if ($maybe) { // placed on the same line do_it_now(); again(); } else { abort_mission(); } finalize(); }
同時注意它們使用4個空格而不是Tab來縮進。
這裏有一個維基百科的文章,裏面有許多不一樣排版樣式的例子。
爲代碼添加註釋是效果顯著的;可是,它可能太過或者只是多餘的文本。像以下例子:
// get the country code $country_code = get_country_code($_SERVER['REMOTE_ADDR']); // if country code is US if ($country_code == 'US') { // display the form input for state echo form_input_state(); }
若是註釋內容都是顯而易見的,它們並無提升工做效率。若是你必需要註釋這些代碼,你能夠簡單的把它們合併在一行:
// display state selection for US users $country_code = get_country_code($_SERVER['REMOTE_ADDR']); if ($country_code == 'US') { echo form_input_state(); }
肯定的任務多半須要多行代碼。使用一些空白將這些任務的代碼分隔爲幾段是一個好主意。
這是一個簡單的示例:
// get list of forums $forums = array(); $r = mysql_query("SELECT id, name, description FROM forums"); while ($d = mysql_fetch_assoc($r)) { $forums []= $d; } // load the templates load_template('header'); load_template('forum_list',$forums); load_template('footer');
在每一段以前添加註釋也加強了視覺上的分隔。
PHP有些時候在遵照命名一致性方面有很大問題:
首先,這些命名必須有單詞的分界線。有兩種流行的選擇:
像我以前提到的同樣,採用不一樣的命名選擇會建立和排版樣式相似的情形。若是一個已有的項目遵守一個肯定的習慣,你必須遵照它。同時,某些語言平臺傾向於使用特定的命名規則。例如Java裏,大多數代碼使用駱駝命名法;在PHP裏大多采用下劃線命名法。
它們也能夠混用。一些開發者喜歡在程序函數和類名上使用下劃線命名,可是在類方法名上使用駱駝命名。
class Foo_Bar { public function someDummyMethod() { } } function procedural_function_name() { }
因此,沒有明顯的「最好的」樣式,只須要保持一致。
DRY即不要重複你本身。也被稱爲DIE:重複是惡魔。
這個原則規定:
「在一個系統裏每個知識的片斷必須有一個單1、明確、權威的表現。」
大多數應用程序(或者一般的計算機)的目的是讓重複的任務自動化。這個原則在全部的代碼,即便Web程序中也應該保持。代碼的相同片斷不該該屢次重複。
例如,大多數Web程序由許多頁面組成。這些頁面極可能包含相同的元素。頁頭和頁腳常常符合這個條件。複製和粘貼這些頁頭和頁尾到每個頁面中不是一個好主意。這是Jeffrey Way解釋如何在CodeIgniter裏建立模版的連接。
$this->load->view('includes/header'); $this->load->view($main_content); $this->load->view('includes/footer');
太多層的嵌套會形成代碼閱讀和跟蹤困難。
function do_stuff() { // ... if (is_writable($folder)) { if ($fp = fopen($file_path,'w')) { if ($stuff = get_some_stuff()) { if (fwrite($fp,$stuff)) { // ... } else { return false; } } else { return false; } } else { return false; } } else { return false; } }
爲了可讀性,一般須要修改代碼來減小嵌套的層數。
function do_stuff() { // ... if (!is_writable($folder)) { return false; } if (!$fp = fopen($file_path,'w')) { return false; } if (!$stuff = get_some_stuff()) { return false; } if (fwrite($fp,$stuff)) { // ... } else { return false; } }
咱們的眼睛對於閱讀高和窄的文本列更感受溫馨。這就是爲何報紙文章看起來像以下樣子的緣由:
避免在一行上編寫過長的代碼是一個最佳實踐。
// bad $my_email->set_from('test@email.com')->add_to('programming@gmail.com')->set_subject('Methods Chained')->set_body('Some long message')->send(); // good $my_email ->set_from('test@email.com') ->add_to('programming@gmail.com') ->set_subject('Methods Chained') ->set_body('Some long message') ->send(); // bad $query = "SELECT id, username, first_name, last_name, status FROM users LEFT JOIN user_posts USING(users.id, user_posts.user_id) WHERE post_id = '123'"; // good $query = "SELECT id, username, first_name, last_name, status FROM users LEFT JOIN user_posts USING(users.id, user_posts.user_id) WHERE post_id = '123'";
同時,若是任何人想要在例如Vim這樣的終端窗口中閱讀代碼,限制每一行的長度在80個字符之內是一個好主意。
理論上,你能夠將整個應用代碼寫在一個文件裏。可是對於閱讀和維護來講是一個噩夢。
在個人第一個編程項目中,我知道建立「包含文件」的含義。可是,我並無好好進行組織。我建立了一個「inc」文件夾,放置了兩個文件:db.php、functions.php。當程序變大時,functions文件也變得愈來愈大並難以維護。
最好的方法之一是採用框架或者模仿它們的文件夾結構。下面是CodeIgniter的文件結構:
一般,變量名應該是描述性的而且包含一個或者更多的單詞。可是,這對臨時變量來講並非必須的。它們能夠短到只有一個單獨字符。
最佳實踐是:對於有一樣職責臨時變量採用統一的命名。這裏有一些我傾向於在代碼裏使用的例子:
// $i for loop counters for ($i = 0; $i < 100; $i++) { // $j for the nested loop counters for ($j = 0; $j < 100; $j++) { } } // $ret for return variables function foo() { $ret['bar'] = get_bar(); $ret['stuff'] = get_stuff(); return $ret; } // $k and $v in foreach foreach ($some_array as $k => $v) { } // $q, $r and $d for mysql $q = "SELECT * FROM table"; $r = mysql_query($q); while ($d = mysql_fetch_assocr($r)) { } // $fp for file pointers $fp = fopen('file.txt','w');
數據庫交互對於大多數Web應用來講是很大一個組成部分。若是你正在編寫SQL查詢,儘可能保持它們可讀。
即便SQL關鍵詞和函數名是大小寫無關的,大寫來將它們從表名和列名中區分出來是一個通用的實踐。
SELECT id, username FROM user; UPDATE user SET last_login = NOW() WHERE id = '123' SELECT id, username FROM user u LEFT JOIN user_address ua ON(u.id = ua.user_id) WHERE ua.state = 'NY' GROUP BY u.id ORDER BY u.username LIMIT 0,20
這是另一個對於全部環境下的絕大多數編程語言都適用的原則。在Web開發中,數據一般意味着HTML輸出。
當PHP許多年前第一次發佈時,它最開始被看做是一個模版引擎。在巨大的HTML文件裏插入一些PHP代碼行是很是普通的。可是,這些年來,事情發生了改變:網站變得愈來愈動態化和功能化。代碼已是Web程序的一個很大的部分,將它們和HTML合併在一塊兒並非一個好的實踐。
你能夠在你的程序中應用這個原則,或者你可使用一個第三方工具(模版引擎、框架或者CMS系統)或者依照它們的習慣。
流行的PHP框架:
流行的模版引擎:
流行的CMS系統:
你能夠選擇不使用一個奇特的模版引擎,取而代之的是在模版文件裏使用純內聯的PHP代碼。這不是必需要違反「數據和代碼分離「,只是內聯代碼是直接和輸出相關的,而且可讀。在這種狀況下你能夠考慮使用交替格式來控制結構。
這是一個示例:
<div class="user_controls"> <?php if ($user = Current_User::user()): ?> Hello, <em><?php echo $user->username; ?></em> <br/> <?php echo anchor('logout', 'Logout'); ?> <?php else: ?> <?php echo anchor('login','Login'); ?> | <?php echo anchor('signup', 'Register'); ?> <?php endif; ?> </div> <h1>My Message Board</h1> <?php foreach($categories as $category): ?> <div class="category"> <h2><?php echo $category->title; ?></h2> <?php foreach($category->Forums as $forum): ?> <div class="forum"> <h3> <?php echo anchor('forums/'.$forum->id, $forum->title) ?> (<?php echo $forum->Threads->count(); ?> threads) </h3> <div class="description"> <?php echo $forum->description; ?> </div> </div> <?php endforeach; ?> </div> <?php endforeach; ?>
這讓你避免了許多大括號。同時代碼看起來和HTML的結構和排版類似。
面向對象編程能夠幫助你建立結構化代碼。可是這不表明你徹底排除程序化編程。事實上建立二者混合的風格是很是棒的。
描述數據,一般是數據庫裏的數據,必須使用對象。
class User { public $username; public $first_name; public $last_name; public $email; public function __construct() { // ... } public function create() { // ... } public function save() { // ... } public function delete() { // ... } }
程序化方法經常使用於能夠獨立執行的特定任務。
function capitalize($string) { $ret = strtoupper($string[0]); $ret .= strtolower(substr($string,1)); return $ret; }
開源項目是許多開發者一塊兒構建的。這些項目必須保持高度的代碼可讀性,以便他們能夠儘量高效的協同工做。
所以,通讀這些項目的源代碼來觀察這些開發者是如何工做的是很是棒的方法。
當你「重構「,你在不改變功能的狀況下調整代碼。你能夠把它看做是「清理」,爲了改進代碼質量和可讀性。
這並不包括bug的修復或者添加新功能。你能夠重構你以前編寫的代碼,當它們在你頭腦你還保持新鮮的時候,以便於你兩個月之後有可能回顧代碼時更加可讀和可重用。就像那句格言所說的同樣:「儘早重構,常常重構「。