当前位置: 首页 > 编程日记 > 正文

《The Art of Readable Code》 读书笔记 01

放假前在学校图书馆借了一本新书《The Art of Readable Code》,寒假回来看看,写写其中的Key Idea 、summary和一些读书笔记。

    Preface

    前言部分主要概况讲了本书的核心思想——Code shoule be easy to understand。接着探讨什么是好代码,是内容紧凑还是对每个过程都详细阐释?从而引发出核心概念:Code should be written to minimize the time it would take someone else to understand it.(代码应让人在尽可能短的时间内理解),这个人,很有可能就是以后的自己。

     通俗来说,简短的代码总是比长代码好读懂,但是不能因为追求简短而使代码难于理解。因此,尽管要追求代码的短小精悍,更应该在最小化理解代码的时间。同 时,易于读懂的代码通常更加易于优化,有更好的架构,易于测试.etc。Easy to understand,对代码来说,是最核心的,因此,当遇到其他冲突要素时,不能忘记这一点。


   Part One:  Surface-Level Improvements

     Packing information into names

    1. 选用明确词汇。

    比如,“get”就不是一个很明确的词汇,比如 def GetPage(url): 函数,我们将不能明确从哪里获取,是本地缓存还是数据库亦或是网络。比如从网络获取,我们可以选用fetch,download去替换。FetchPage,DownloadPage…….。一次类推,比如表达树的size,可以用NumNodes,进程stop可以用kill。另外,我们可以选用更加丰富的词汇来表达:

WordAlternatives
senddeliver,dispatch,announce,distribute,route
findsearch,extract,locate,recover
startlaunch,create,begin,open
makecreate,set up,build,generate,compose,add,new

    Key idea: It's better to be clear and precise than to be cute. 清晰准确,甚于灵巧可爱。

    2. 避免使用泛化的名字,比如tmp,retval,foo等

    比如retval(I'm a return value),我们应该选用描述变量值含义的词汇。比如tmp,它应仅仅用于短期存在或临时性是该变量的重要成分(此时tmp应该作为前缀或后缀,tmp_file),除此之外,我们都应选用能够描述含义的词汇。

    另外,对于循环迭代中的 i,j,k,iter等,在多重循环时可以附加一些index的讯息。比如

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++)

    这里,i可以改为 ci, j改为 mi, k改为 ui,这样,就能够很清晰的明白每个循环变量是什么意思了。类似的,选用 r -> row, c -> column 用在矩阵运算中。

    3. 选用具体的名字而不是抽象的

     比如,ServerCanStart()是个抽象的名称,可以改成CanListenOnPort()这个具体的名称。

    为防止类拷贝,可以将拷贝构造函数和=运算私有化,谷歌中以前使用宏DISALLOW_EVIL_CONSTRUCTORS(ClassName),这里

#define DISALLOW_EVIL_CONSTRUCTORS(ClassName) \ClassName(const ClassName&); \void operator=(const ClassName&);

    这里,宏名选用"evil"不太好,这里可以改为 DISALLOW_COPY_AND_ASSIGN(ClassName) …

  4. 给名字添加额外讯息。

    比如,string id; 这里的id没有附加更多的讯息,若该id是由十六进制数构成,可以改成 hex_id等。

    比如,变量是一个表示度量值的数,比如时间,重量。我们可以加上单位信息,这样对于代码就能够更好的看懂。delay -> delay_ms, angle -> degree_cw (顺时针)。

    同时,对于某些应用场合,需要给变量加上处理状态的讯息或编码的讯息,比如 password -> plaintext_password,表示未经处理的密码字符串。html -> html_utf8 表示

该网页是采用utf8编码等。

   5.选用适合长度的名字

    通过变量的使用范围跨度来度量。若是范围小,比如几行之间,可以选用短名字。

    若是范围跨度大,可以选用长点的名字表达更加丰富的信息,但是不能太长了。另外,编程敲打长名字,可以使用编辑器的自动补全功能。 vim中可以用ctrl+p,甚至安装插件,按个tab就行。

    另外,对于缩写,应该遵循通用的简称,比如string -> str, document-> doc, evaluation -> eval 等。

    最后,可以抛弃无用的字眼,比如在类型转换, ConvertToString() -> ToString(),这里,To就有convert的含义,所以convert可以抛弃。

   6. 选用不同名字格式表达不同的含义,可以参考google编程风格。

  

static const int kMaxOpenFiles = 100;  // 常量使用前面加k表示,而不是全大  写,同宏名区别class LogReader{    // 类名首字母大写的驼峰法public:void OpenFile(string local_file);  // 局部变量名使用小写字母加下划线private:int offset_;    // 类成员变量使用小写,并在最后添加下划线DISALLOW_COPY_AND_ASSIGN(LogReader); // 宏名全大写
};

名字不能被误解

    key idea: Actively scrutinize(细查) your names by asking yourself, "What other meanings could someone interpret from this name?"

    比如: filter() 过滤。 这里,过滤有双重理解,"to pick out"  or "to get rid of" 。 若是 to pick out ,则改为 select, 若是 to get rid of, 则改为 exclude。

   比如: clip() 剪除      def clip(text, max_len):   有两种含义: 1、remove length from the end   2、 truncates to a maximum length。同时,这里,max_len也是容易让人歧义,是指 words还是bytes亦或是characters,应该改为 max_chars 等。

   filter, length, limit是容易模棱两可的词汇。

   1、涉及最大最小的限制时,添加max或min的前缀,比如 max_len, min_len。

   2、前闭后闭范围[...],使用 first 和 last

   3、前闭后开范围[...),使用 begin 和 end

   4、布尔值,添加前缀 has, is, can, should 等表明,布尔值命名不要使用否定式。ex. bool disable_ssl = fasle; ->  bool use_ssl = true;

   5、不滥用约定熟成的变量名。 比如 get*(), 他应该是运算复杂度为 O(1)的方法,若是使用 getAverage(),它是一个复杂度为O(n)的运算,会误导人们去使用,导致增加时间消耗,应改为computeAverage(),这样,就告诉人们得到average是要compute的,而不是伸手既得的。

        比如 list::size(),在stl中,为了统一命名,对list容器采用了size()方法,而list::size()方法是O(n)的,vector::size()是O(1)的,这样就会误导用户使用size()增加时间消耗。庆幸的是,最新的C++标准中,list::size()是O(1)的。

        另外,在有多个候选名可以用时,要选择最能表达动机的词语。


    Aesthetics 美感

   Good source code should be just as "easy on the eyes"

   1、重排换行位置得到更加一致和紧致的效果

public class PerformanceTester{// TcpConnectionSimulator(Throughput, lantency, jitter, packet_loss)//                            [kbps]   [ms]       [ms]    [percent]public static final TcpConnectionSimulation wifi = new TcpConnectionSimulator(              500,        80,                 200,          1);public static final TcpConnectionSimulation t3_fiber= new TcpConnectionSimulator(             45000,      10,                     0,          0);public static final TcpConnectionSimulation wifi = new TcpConnectionSimulator(              100,        400,                 250,         5);
}

  当然,这里忽略了敲打空白和排列的时间,上述代码肯定很容易看懂。

   2、对于重复形式的代码,编写函数,且函数命名有意义。

   3、多形参采用列对齐排列。如上面的代码。

   4、选择一个固定的排列顺序,比如按字母,按逻辑顺序,并在而后的调用中保持。

   5、大段代码按逻辑组合,添加空白行和功能注释,使代码层次鲜明。

   6、选用一致的代码风格,并保持。


 Knowing what to Comment

 key idea: The purpose of commenting is to help the reader know as much as the writer did.

 1、什么不该被注释?

   a. 不注释能从代码本身迅速传达含义的语句。

   b. 不要为了注释而注释,避免这点,应该详细添加表达代码逻辑层次的注释。

   c. 不为烂名字而注释,而应该改名字。 good code > bad code + good comments!

  2、 记录你的想法

    a. 包含“导演评论”,即洞察性的语句,能够避免重复劳动、无意义劳动。同时指出改进的方向。

    b. 注释代码中的“瑕疵”

MarkerTypical meaning
TODO:半成品
FIXME:此处代码有问题
HACK:诚然不雅的解决问题
XXX:危险,主要问题在这

   c. 对常量注释()

   d.站在代码阅读者的角度去看

       猜测读者会在哪里产生疑惑。

       ex.

struct Recoder{vector<float> data;...void Clear(){vector<float>().swap(data);  // why not just data.clear()?   
    }
}

上述代码中,读者将不太了解为什么要调用空对象的swap方法释放data的空间。这里,是唯一的方式强行使vector将自己的内存归还内存池。所以,应补上注释如下:

// Force vector to relinquish its memory (Look up "STL swap trick")
vector<float>().swap(data);

     另外,有一些函数的行为将占据大量的系统资源,需要注释说明其运行环境及限制条件。

    e.大框架注释   对整个系统的每个关键环节,节点间联通关系等进行注释,让后来人能够理解代码的意图。

    f. 对代码块进行注释,使读者免于陷入细节中。


    Making Comments Precise and Compact 注释更加精确紧致

  key idea: Comments should have a high information-to-space ratio。 注释应该言简意赅

   1.保持代码紧凑。

// The int is the CategoryType
// The first float in the inner pair is the 'score'
// The second is the  'weight'

typedef hash_map<int, pair<float, float> > ScoreMap;// 这里,三行的注释可以归结成一行
// CategoryType->(score, weight)

  2.避免模棱两可的代词。

// Insert the data into the cache, but check if it's too big first

这里,it的指代不明,可能是data,也可能是cache。改进方法:

// 1.将代词补全
// but check if the data is too big first// 2.将句型重新排列
// If the data is small enough, insert it into the cache

   3. 雕琢粗糙的表达。 这个,看语文功底了.

   4.精确描述函数行为。 比如一个函数要返回文件的行数,不能仅仅这样注释,应该要注释如何度量何为“行”的标准,如度量'\n'个数(unix),‘\n\r’(Windows)。

   5. 采用一些输入输出的样例来阐释特殊情况。这一点,如同acm的题,sample input, sample out ...

   6. 陈述代码的高层意图

   7. 可以对神秘的函数参数进行内联注释, e.g. Functoin(/* arg = */ ...)

   8. 选用信息密度强的词汇表达。也就是言简意赅。

转载于:https://www.cnblogs.com/IntellX/archive/2013/02/07/2908108.html

相关文章:

吴裕雄 10-MySQL插入数据

语法以下为向MySQL数据表插入数据通用的 INSERT INTO SQL语法&#xff1a;INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );如果数据是字符型&#xff0c;必须使用单引号或者双引号&#xff0c;如&#xff1a;"value"。 通…

编译内核指定模块,筛选当前模块依赖的组件

关于内核模块编译的过程中&#xff0c;往往我们仅仅需要其中一个小的模块&#xff0c;但是却因为内核源码的庞杂而止步与模块依赖的筛选过程中。 为了更加便捷得对内核各个模块进行管理&#xff0c;这里提供一个小脚本来进行指定模块相关得模块留存&#xff0c;不相关的模块代码…

计算机启动和操作系统加载小话

整个启动和加载过程可分为若干步骤&#xff0c;或者称为若干个状态&#xff0c;或者快照&#xff0c;下面的每一段都是描述一个快照。&#xff08;类似自动状态机&#xff09; 1、电源稳定&#xff08;POWER GOOD&#xff09; 按下启动键后&#xff0c;电源首先启动。为了保证安…

java清空栈_java - 如何使用Intent.FLAG_ACTIVITY_CLEAR_TOP清除活动堆栈?

java - 如何使用Intent.FLAG_ACTIVITY_CLEAR_TOP清除活动堆栈&#xff1f;我已经阅读了几篇关于使用它的帖子&#xff0c;但必须遗漏一些因为它不适合我。 我的活动A在清单中有launchmode “singleTop”。 它启动活动B&#xff0c;启动模式“singleInstance”。 活动B打开浏览器…

「学习笔记-Linux」学习Shell Script

学习Shell Script Table of Contents 1 什么是Shell Scipt 1.1 程序书写1.2 程序执行2 简单Shell练习 2.1 例1 接收用户输入2.2 例2 按日期建立相似名字的文件3 判断式 3.1 测试文件是否存在3.2 test常用选项 3.2.1 文件类型3.2.2 权限3.2.3 文件新旧比较3.2.4 整数&#xff0c…

django admin组件

admin实例 from django.contrib import admin from app01 import models from django.utils.safestring import mark_safe # Register your models here. class UserInfoConfig(admin.ModelAdmin):# 自定义显示的东西def xxx(self):return mark_safe(<a href>xx</a>…

C语言网络编程:close或者shutdown断开通信连接

文章目录前言close函数介绍shutdown函数介绍前言 这里在主要通过实例进行描述close函数在网络编程中的使用 TCP编程模型中客户端或者服务器只要主动通过close发起断开连接的请求&#xff0c;则通信连接可以中断。 可以通过在主进程中抓取通信端的断开信号&#xff0c;比如SIGI…

Await, and UI, and deadlocks! Oh my!

It’s been awesome seeing the level of interest developers have had for the Async CTP and how much usage it’s getting. Of course, with any new technology there are bound to be some hiccups. One issue I’ve seen arise now multiple times is developers acc…

传智播客java基础的习题_传智播客java基础班(集合与IO)阶段测试题

本帖最后由 zhaodecang 于 2016-6-8 19:38 编辑单选题&#xff1a;(每道题目2分)1. ArrayList类的底层数据结构是( )a) 数组结构b) 链表结构 c) 哈希表结构 d) 红黑树结构2. LinkedList类的特点是( )a) 查询快b) 增删快c) 元素不重复 d) 元素自然排序3. Vector类的特点…

$@ 与 $* 差在哪?

$ 与 $* 差在哪&#xff1f; 要说 $ 与 $* 之前&#xff0c;需得先从 shell script 的 positional parameter 谈起...我们都已经知道变量(variable)是如何定义及替换的&#xff0c;这个不用再多讲了。但是&#xff0c;我们还需要知道有些变量是 shell 内定的&#xff0c;且其名…

[源码和文档分享]基于Netty和WebSocket的Web聊天室

一、背景 伴随着Internet的发展与宽带技术的普及&#xff0c;人们可以通过Internet交换动态数据&#xff0c;展示新产品&#xff0c;与人进行沟通并进行电子商务贸易。作为构成网站的重要组成部分&#xff0c;留言管理系统为人们的交流提供了一个崭新的平台。同时&#xff0c;聊…

t-tcpdump

文章目录写入和读取数据包抓取数据包抓取指定网卡流量指定数据的输出格式数据包抓取的方向输出信息的详细程度的可控选项抓取指定协议的数据包表达式介绍逻辑连接符的使用type的确定写入和读取数据包 在工作或者生活中的网络故障排除时最有力的方式就是抓包分析网络状况&#…

java jdk 8u111_8u111-jdk-alpine在java开发中的NullPointerException错误解决方案

问题描述在部署一个验证码服务的容器服务时遇到了一个空指针错误&#xff0c;错误代码为&#xff1a;java.lang.NullPointerExceptionat sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguratio…

sprintf函数做什么用?

sprintf函数原型为 int sprintf(char *str, const char *format, ...)。作用是格式化字符串&#xff0c;具体功能如下所示&#xff1a; &#xff08;1&#xff09;将数字变量转换为字符串。 &#xff08;2&#xff09;得到整型变量的16进制和8进制字符串。 &#xff08;3&#…

Yii学习笔记【3】

加载控制器及其方法&#xff1a; 根据route信息&#xff0c;获得当前控制器| 初始化当前控制器&#xff0c;CController::init()&#xff0c;默认为空| 执行当前控制器&#xff0c;CController::run()||----> 创建action&#xff0c;为空则默认为index|得到CInlineAction的实…

验证码相似问题

产生随机验证码时&#xff0c;类似数字1和小写字母l经常容易让人混淆分不清楚&#xff0c; 因此&#xff0c;产生随机验证码时应避免此情况 1&#xff08;一&#xff09;、l&#xff08;哎哦&#xff09;、I &#xff08;哎&#xff09;中三个任意两个或者全部不可同时存在 0&a…

C语言网络编程:accept函数详解

文章目录前言函数描述代码实例如何得到客户端的IP 和 端口号前言 当使用tcp服务器使用socket创建通信文件描述符&#xff0c;bind绑定了文件描述符&#xff0c;服务器ip和端口号&#xff0c;listen将服务器端的主动描述符转为被动描述符进行监听之后&#xff0c;接口accept通过…

java 声明静态类_java静态类声明--java类可以声明为static吗

为了理解static关键字在类声明中的使用&#xff0c;首先我们需要了解类声明。有两种类&#xff0c;一种是top-level class&#xff1b;一种是inner class。Top-level classestop-level class可以被声明为包成员&#xff0c;每一个top-level类对应于一个文件名与类名相同的java文…

单元测试资料汇总

从安装到配置 首先到官网http://www.nunit.org/下载如下图的资料&#xff0c;安装NUnit-2.6.1.msi包。 然后挂在VS2010外部工具这个地方来使用&#xff0c;工具—>外部工具—>添加—>标题&#xff1a;Nunit—>命令&#xff1a;安装路径—>确定。 然后打开Nunit&…

rhel5+nis+autofs+nfs

创建NIS服务器用户&#xff0c;用于客户端登陆 NIS服务器相关包&#xff1a;ypserv、ypbind(在RHEL5中默认已安装)、yp-tools(在RHEL5中默认已安装)。 运行nisdomainname test.com并把加入到如下位置 设置NIS服务器的域名 在NIS环境中将以NIS服务器上的所有用户用于NIS环境中所…

Beta冲刺 (1/7)

Part.1 开篇 队名&#xff1a;彳艮彳亍团队 组长博客&#xff1a;戳我进入 作业博客&#xff1a;班级博客本次作业的链接 Part.2 成员汇报 组员1&#xff08;组长&#xff09;柯奇豪 过去两天完成了哪些任务 熟悉并编写小程序的自定义控件展示GitHub当日代码/文档签入记录接下来…

C语言网络编程:listen函数详解

文章目录前言函数描述代码实例TCP服务器为什么调用listen前言 根据TCP编程模型中我们可以看到之前的socket和bind接口是tcp服务器在为接收客户端的链接做准备&#xff0c;保证tcp的面向字节流&#xff0c;面向连接的可靠通信服务正常进行。接下来的listen端口则为我们进行三次…

MVC页面加载速度优化小记

前言&#xff1a;最近做一个地图展示页面&#xff0c;业务初期没什么问题&#xff0c;运行一阵后报错&#xff1a; Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLeng…

java生成函数excel_java实现在excel中创建及读取公式

操作excel表格用公式来处理数据时&#xff0c;可通过创建公式来运算数据&#xff0c;或通过读取公式来获取数据信息来源。这里使用了java类库(Free Spire.XLS for Java 免费版)获取文件包后&#xff0c;解压&#xff0c;将lib文件夹下的jar文件导入Java程序。如图&#xff1a;(…

实战:使用TCP/IP筛选保护服务器安全

使用TCP/IP筛选保护服务器安全 对于部署在Internet的服务器&#xff0c;安全是必须要考虑的事情。为了降低服务器受***的危险&#xff0c;停止不必要的服务或在本地连接的TCP/IP属性中只打开必要的端口。 如图2-127所示&#xff0c;实验环境为Server的IP地址192.168.1.200&…

python中的协程(二)

协程 1、协程&#xff1a; 单线程实现并发 在应用程序里控制多个任务的切换保存状态 优点&#xff1a; 应用程序级别速度要远远高于操作系统的切换 缺点&#xff1a; 多个任务一旦有一个阻塞没有切&#xff0c;整个线程都阻塞在原地&#xff0c;该线程内的其他的任务都不能执行…

C语言网络编程:bind函数详解

文章目录函数功能函数头文件函数使用函数参数函数举例为什么需要bind函数服务器如何知道客户端的ip和端口号htons函数htons兄弟函数htonl,ntohs,ntohl为什么要进行端口的大小端序的转换inet_addr函数函数功能 bind API能够将套接字文件描述符、端口号和ip绑定到一起 注意&…

java flex 图片上传_flex上传图片到java服务器

今天弄flex上传图片到java&#xff0c;现在弄成功&#xff0c;中间也经常一点小波折&#xff0c;现记录一下。重点在java侧的实现。flex侧&#xff1a;文件上载到在url参数中传递的URL。该URL必须是配置为接受上载的服务器脚本。Flash Player使用HTTP POST方法上载文件。处理上…

开发者怎么样做到盈利

开发者如何赚钱? 不可回避的一点就是&#xff0c;开发者的产品要有足够好的用户体验。假设你会做手机游戏&#xff0c;那么把手游做好了之后用户的粘性很大&#xff0c;如果你做应用&#xff0c;那么你的应用下载会对用户产生有价值的东西。 其实如果你的产品真的有价值&#…

如何在Windows Azure VM上的SQL Server和Windows Azure SQL Database两者中做出选择

作者信息&#xff1a;本篇文章是由SQL Server Cloud Infrastructure Team的 Madhan Arumugam 和 Guy Bowerman共同著作。 简介 把SQL 数据托管在哪里&#xff0c;Windows Azure 为您提供了两个选择&#xff0c;VM上的SQL Server&#xff08;以下简称 SQL/VM&#xff09;和 Wind…