代码可读性在编程界是个全球性的话题了,它也是开发者在入门时最早接触到的东西之一。对一个完成的产品来说,代码具有可读性和可维护性在某种程度上来说是件值得自豪的事。我们可以跟其他人分享这些代码,捐献给其他的项目,也可以重用我们几个月前甚至几年前写的应用上的代码。
这篇文章将会详细介绍编写可读代码的15个最佳习惯。
1.注释和文档(Commenting & Documentation)
IDE(Integrated Development Environment)在最近几年有着不少的进步,使得注释代码比以前更加的有用。根据一些标准来注释你的代码可以让IDE和其他工具通过不同途径去使用它们。
看看这个例子:

我在定义方法的时候添加的注释可以在使用的时候预览到,就算这个方法是在其他文件定义的。
这是另外一个例子,我调用了一个第三方库的方法:

在这两个例子里,注释(或者文档)所有使用的风格基于PHPDoc,使用的IDE为Aptana.
2. 一致的缩进(Consistent Indentation)
我假定你们都知道应该缩进代码。但是值得一提的是,保持代码缩进方式的一致性是个好主意。
我们有多种的方式去缩进代码。
样式1:
function foo() {
if ($maybe) {
do_it_now();
again();
} else {
abort_mission();
}
finalize();
}
样式2:
function foo()
{
if ($maybe)
{
do_it_now();
again();
}
else
{
abort_mission();
}
finalize();
}
样式3:
function foo()
{ if ($maybe)
{ do_it_now();
again();
}
else
{ abort_mission();
}
finalize();
}
我以前使用样式2的方式,但是最近又转为用样式1。但是这只是个人喜好问题,并没有一种最好的方式大家都应该去使用。实际上,最好的方式就是使用一致的方式。如果你是团队里的一员或者你在为某个项目写代码,那么你应该跟随已经在项目里使用的方式进行编码。
并不是每个人使用的缩进方式都完全一样的。有时他们会揉合不同的规则进去。举个例子,在PEAR Coding Standards里,括号 "{" 跟 control structures在同一行,但是如果是跟在 function definitions后则换行显示。
PEAR使用的样式:
function foo()
{ // 放在下一行
if ($maybe) { // 放在同一行
do_it_now();
again();
} else {
abort_mission();
}
finalize();
}
需要注意的是,他们使用空格来缩进,而不是用tab。
这是一篇Wikipedia的文章,里面有各种不同缩进方式的实例。
3. 避免过分的注释(Avoid Obvious Comments)
注释代码是件好事,但是它有可能会过量或者完全多余。看这个例子:
// 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();
}
4. 代码分组(Code Grouping)
通常,有些任务只需要几行的代码就能实现,那么我们最好用一些空白把他们分隔成块。
这是一个简单的例子:
// 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');
在每一块前添加注释也可以加强分隔的效果。
5. 一致的命名方法(Consistent Naming Scheme)
PHP自身也有过错,因为它的命名规则也没统一:
- strpos() vs. str_split()
- imagetypes() vs. image_type_to_extension()
首先,名字里的单词要有边界(word boundaries)。有两个比较流行的方法:
- 骆驼命名法(camelCase):除了第一个单词外,其他单词的首字母都大写。
- 下划线命名法(underscores): 各个单词间用下划线进行连接,像: mysql_real_escape_string()这样。
就像之前我说到的缩进方式一样,如果现有的项目已经用了其中一种方式,那么你也要跟着用。同样的,有些编程语言倾向于用其中一种命名方式。例如Java里,大部分代码使用骆驼命名法,而在PHP里则使用下划线命名法。
它们同样可以混合在一起用。有些人喜欢把下划线命名法应用到过程函数(procedural functions)和类名上,然后把骆驼命名法用到类里面的方法名上:
class Foo_Bar {
public function someDummyMethod() {
}
}
function procedural_function_name() {
}
再次声明,除了保持一致性,并没有最好的命名方式。
6.DRY 原则(DRY Principle)
DRY的意思是“Don't Repeat Yourself”,它还有个叫法DIE: Duplication is Evil。
这个原则说到:
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."
太多数应用程序(普通意义上的电脑)的目的是自动完成那些重复的工作。这个原则应该在所有的代码甚至是网络应用上应用,相同的代码不应该被一而再再二三的重复。
举个例子,大多数的网站都由很多的页面组成,这些页面都包含一些共同的元素。Header跟Footer算是代表了,在每个页面都复制粘贴这些header跟footer不是个好主意。这是Jeffrey Way讲解的如何在CodeIgniter(一PHP框架)里创建模板。
$this->load->view('includes/header');
$this->load->view($main_content);
$this->load->view('includes/footer');
7. 避免深层的嵌套(Avoid Deep Nesting)
太多层次的嵌套会使得代码难以阅读和理解。
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;
}
}
8. 限制行宽(Limit Line Length)
我们阅读比较长和窄的文字的时候眼睛会比较舒服,这也是报纸上的文章像下图那样显示的原因:

避免在一行里编写很长的代码是个好习惯。
// 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'";
如果你按照“避免深层嵌套”的建议,那么它也会帮助你把每一行代码的长度控制在一个合理的大小。
同样的,如果有人通过某终端窗口阅读代码,例如使用VI的用户,那么把每行的长度限制在80个字符左右会好点。
9. 文件和文件夹的组织(File and Folder Organization)
技术上,你可以把整个应用写在一个文件里,但是这将会是一个噩梦去阅读和维护它。
在我的第一个编程项目的时候,我就知道要创建“include files”。但是那时我还不会去组织它们。我创建了一个“inc”文件夹唉,里面放了两个文件:db.php和function.php。随着应用程序的增大,文件变得越来越大和难以维护。
其中一个最被认可的方法是使用框架或者模仿他的文件夹结构。下面是CodeIgniter里文件的组织结构:

10. 统一临时变量名(Consistent Temporary Names)
通常来说,变量命应该易懂而且包含一个以上的单词。但是,对来临时变量来说这并不是必要的,它们可以用简短的字符表示。
对具有相同功能的临时变量使用统一的名字是个好习惯。下面是几个我喜欢在我代码里用到的例子:
// $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');
11. SQL的关键字要大写(Capitalize SQL Special Words)
跟数据库打交道是大多数网络应用的重要组成部分。如果你要写一些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
12. 分开代码跟数据(Separation of Code and Data)
这是另外一个适用于几乎所有程序语言的原则。对于网页开发来说,数据通常指HTML输出。
在很多年前PHP推出的时候,它主要被当作是一个模板引擎。通常会看到一个很大的HTML文件里包含了几行的PHP代码。然而,这一切都在几年后改变了,网站变得越来越动态,功能越来越多,PHP代码已经是网络应用中的一个巨大的组成部分,所以在HTML里写PHP代码已经不合时宜了。
你可以自己把这个原则应用到你的应用里,或者你可以使用第三方的工具(模板引擎,框架或者CMS)并跟随它们的习惯。
流行的PHP框架:
流行的模板引擎:
流行的CMS:
(这里原文也没有,大概是作者遗漏了……)
13. 在模板里使用替代语法(Alternate Syntax Inside Templates)
这个并没有违法“分开代码跟数据”的原则,只要这些代码跟输出有直接的联系同时具有可读性。在这种情况下,你应该考虑使用流程控制的替代语法(alternate syntax for control structures).
下面是一个例子:
Hello, username; ?>
|
My Message Board
title; ?>
Forums as $forum): ?>
id, $forum->title) ?>
(Threads->count(); ?> threads)
description; ?>
这样你就可以避免使用大括号了。同时,代码也跟HMTL的结构和缩进方式相似了。
14. 面向对象 VS. 面向过程(Object Oriented vs. Procedural)
面向对象编程可以帮你创建结构良好的代码,但是这并不代表你要去完全否定面向过程的编程。事实上结合两者也有好处。
对象应该用在封装数据库的数据上。
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;
}
15. 读开源代码(Read Open Source Code)
开源项目基本上都是由很多开发者共同开发的,这些项目需要保持良好的可读性使得小组里的人可以尽量高效的协同工作。因此,有时打开这些项目看看开发者们在干些什么也是件好事。

16. 代码重构(Code Refactoring)
重构意味着在不改变功能的情况写改写代码。你可以把它想象为“清理”代码改进代码的可读性和质量。
重构不包括修复bug和添加新功能。你可以重构你前一天写的代码,这些代码在你脑子里依然很清晰,这样当在你两个月后再看这些代码的时候这些代码就会更加的易读和进行重用。因此有句格言:“早点重构,经常重构。(refactor early, refactor often.)”
你可能在重构代码的时候应用这些“最佳习惯”。"
Translated by icyfire @ Canaand on Thur, 21th Jan.
翻译水平有限,自娱自乐。
原文:Top 15+ Best Practices for Writing Super Readable Code
Author: Burak Guzel
Burak Guzel is a full time PHP Web Developer living in Arizona, originally from Istanbul, Turkey. He has a bachelors degree in Computer Science and Engineering from The Ohio State University. He has over 8 years of experience with PHP and MySQL. You can read more of his articles on his website at PHPandStuff.com and follow him on Twitter here.