The Art of Readable Code

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

 

Key Idea

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 sea​​rch, 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

Pick a name that describes the entity's value or purpose

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, ...)
Advice

The name tmp should be used only in cases when being short-lived and temporary 
is the most important fact about that variable.

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_ici , 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  :)

 

posted @ 2013-04-25 12:28 重庆Debug 阅读(...) 评论(...) 编辑 收藏