The Art of Readable Code
Author: Dustin Boswell, Trevor Foucher
- Part 1: Surface-Level Improvements
- Part 2: Simplifying Loops and Logic
- Part 3: Reorganizing Your Code
- Selected Topic: Readability and Testability
Pack Information into your names
这里提供了六个准则,让你在命名程式中的变量(variable)、函数(function)、类(class)时,能取个好名
1. Choose Specific Words
命名首要规则,选用的字词要能够「带有殊别意义的」。
def GetPage(url): ... |
Get这个字眼其实没有太多资讯,因为你无法得是在本地端取得page、还是从资料库或经由网路下载得到呢?
更好的字眼,应该是FetchPage()或是DownloadPage()
class BinaryTree { int size(); }; |
你觉得size()会回传什么值呢?是二元树高度(height)、节点的个数(node)、或是占用的内存呢?
因此,更适当的命名应该是Height() , NumNodes() , MemoryBytes()。
这表列出了一些值得考虑的替代字:
Word | Alternatives |
send | deliver, dispatch, announce, distribute, route |
find | search, extract, locate, recover |
start | launch, create, begin, open |
make | create, set up, build, generate, compose, add, new |
2. Avoiding Generic Names Like tmp and retval
var euclidean_norm = function (v) { var retval = 0.0 ; for (var i = 0 ; i < v.length; i += 1 ) retval += v[i] * v[i]; return Math.sqrt(retval); }; |
出现retval这样的名称时,通常意味着-已经是星期五下午了,你正等着五点一到就下班闪人,不想在这该死的变量名称上伤脑筋了。
但是你静下心来思考十分钟,发现它可以有更好的名字,像是sum_squares -这个变量的用途(purpose)就是在累加一些平方数。
试比较以下两个例子,你不小心写错累加的逻辑。但是藉由好的命名,可以让你轻易找出这只臭虫!
retval += v[i]; // It seems god-damn-it right... sum_squares += v[i]; // Where's the "square" that we're summing? Bug! |
tmp也是一个不经大脑情况下常使用的名称,如下面的例子,一个比较好的命名可以是user_info。
String tmp = user.name(); tmp += " " + user.phone_number(); tmp += " " + user.email(); ... template.set( "user_info" , tmp); |
那tmp_file是一个好名字吗?是的,tmp部份说明了这变量是暂时性使用,file部份说明了这是一个「档案」物件。
tmp_file = tempfile.NamedTemporaryFile() ... SaveData(tmp_file, ...) |
如果只是个tmp的命名,在看到下列这一行程式时,我们其实无法知道tmp是档案物件或是档案名称?
SaveData(tmp, ...) |
i, j, k 是常见的回圈变量:
for ( int i = 0 ; i < clubs.size(); i++) for ( int j = 0 ; j < clubs[i].members.size(); j++) for ( int k = 0 ; k < users.size(); k++) if (clubs[i].members[k] == users[j]) cout << "user[" << j << "] is in club[" << i << "]" << endl; |
但是你看的出来,这一行写错了吗?
if (clubs[i].members[k] == users[j]) |
让我们换个更好的命名club_i , member_i , users_i或ci , mi , ui。
hmm...我的视力从来没有像现在这么好过!!!
if (clubs[ci].members[ui] == users[mi]) # Bug! First letters don't match up. if (clubs[ci].members[mi] == users[ui]) # OK. First letters match. |
3. Prefer Concrete Names over Abstract names
DISALLOW_EVIL_CONSTRUCTORS到底在做什么呢?
class ClassName { private : DISALLOW_EVIL_CONSTRUCTORS(ClassName); public : ... }; #define DISALLOW_EVIL_CONSTRUCTORS(ClassName) \ ClassName( const ClassName&); \ void operator=( const ClassName&); |
喔喔喔,EVIL实在太抽像了,我们还是换个字吧:
#define DISALLOW_COPY_AND_ASSIGN(ClassName) |
4. Attaching Extra Information to a Name
string id; // Example: "af84ef845cd8" |
即然id是一个储存16进位型式的字串,那何不取成hex_id更好呢
var start = ( new Date()).getTime(); // top of the page ... var elapsed = ( new Date()).getTime() - start; // bottom of the page document.writeln( "Load time was: " + elapsed + " seconds" ); |
其实getTime()传回的单位是毫秒(milliseconds),所以我们最好在命名时加上ms单位,
这下子就会记得要除上1000才会得到正确的秒数了。
var start_ms = ( new Date()).getTime(); // top of the page ... var elapsed_ms = ( new Date()).getTime() - start_ms; // bottom of the page document.writeln( "Load time was: " + elapsed_ms / 1000 + " seconds" ); |
这里还有一些例子,赶快去丰富你的变量名称吧~
Function parameter | Renaming parameter to encode units |
Start(int delay ) | delay -> delay_secs |
CreateCache(int size ) | size -> size_mb |
ThrottleDownload(float limit) | limit -> max_kbps |
Rotate(float angle) | angle -> degrees_cw |
除了考虑到值的单位(unit),值的encode也是需要注意的:
Situation | Variable name | Better name |
A password is in “plaintext” and should be encrypted before further processing | password | plaintext_password |
A user-provided comment that needs escaping before being displayed | comment | unescaped_comment |
Bytes of html have been converted to UTF-8 | html | html_utf8 |
Incoming data has been “url encoded” | data | data_urlenc |
5. Deciding How Long a Name should be
- Shorter Names Are Okay for Shorter Scope
在这个例子,m的命名是非常行的通,因为它的作用域非常小,只在短短三行之间,一下子就能读懂m是什么以及m的用途。
if (debug) { map<string, int > m; LookUpNamesNumbers(&m); Print(m); } |
但在这样的情形下,我们对m的了解远远不够,需要让命名带着更多资讯。
LookUpNamesNumbers(&m); Print(m); |
- Acronyms and Abbreviations
是否要使用缩写呢?请跟着我大声念三次这条第一守则:would a new teammate understand what the name means?
用eval表示evaluation、doc表示document、str表示string没有太大问题。但是BEManager到底是什么呢?这就莫宰羊了...(其实是*BackEndManager*的缩写)。
更多Garmin缩写字可参考:REF - Garmin Acronyms
6. Use Name Formatting to Convey Meaning
请参考:Google C++ Style Guide :)