不少同行在编写代码的时候每每只关注一些宏观上的主题:架构,设计模式,数据结构等等,却忽视了一些更细节上的点:好比变量如何命名与使用,控制流的设计,以及注释的写法等等。以上这些细节上的东西能够用代码的可读性来归纳。javascript
不一样于宏观上的架构,设计模式等须要好几个类,好几个模块才能看出来:代码的可读性是可以马上从微观上的,一个变量的命名,函数的逻辑划分,注释的信息质量里面看出来的。html
宏观层面上的东西当然重要,可是代码的可读性也属于评价代码质量的一个没法让人忽视的指标:它影响了阅读代码的成本(毕竟代码主要是给人看的),甚至会影响代码出错的几率!java
这里引用《编写可读代码的艺术》这本书里的一句话:git
对于一个总体的软件系统而言,既须要宏观的架构决策,设计与指导原则,也必须重视微观上的的代码细节。在软件历史中,有许多影响深远的重大失败,其根源每每是编码细节出现了疏漏。程序员
所以笔者认为代码的可读性能够做为考量一名程序员专业程度的指标。github
或许已经有不少同行也正在努力提升本身代码的可读性。然而这里有一个很典型的错觉(笔者以前就有这种错觉)是:越少的代码越容易让人理解。objective-c
可是事实上,并非代码越精简就越容易让人理解。相对于追求最小化代码行数,一个更好的提升可读性方法是最小化人们理解代码所须要的时间。算法
这就引出了这本中的一个核心定理:编程
可读性基本定理:代码的写法应当使别人理解它所须要的时间最小化。设计模式
这本书讲的就是关于“如何提升代码的可读性”。 笔者总结下来,这本书从浅入深,在三个层次告诉了咱们如何让代码易于理解:
首先来说最简单的一层如何改进,涉及到如下几点:
关于如何命名,做者提出了一个关键思想:
关键思想:把尽量多的信息装入名字中。
这里的多指的是有价值的多。那么如何作到有价值呢?做者介绍了如下几个建议:
一个比较常见的反例:get
。
get
这个词最好是用来作轻量级的取方法的开头,而若是用到其余的地方就会显得很不专业。
举个书中的例子:
getPage(url)
经过这个方法名很难判断出这个方法是从缓存中获取页面数据仍是从网页中获取。若是是从网页中获取,更专业的词应该是fetchPage(url)
或者downloadPage(url)
。
还有一个比较常见的反例:returnValue
和retval
。这二者都是“返回值”的意思,他们被滥用在各个有返回值的函数里面。其实这两个词除了携带他们原本的意思返回值
之外并不具有任何其余的信息,是典型的泛泛的名字。
那么如何选择一个专业的词汇呢?答案是在很是贴近你本身的意图的基础上,选择一个富有表现力的词汇。
举几个例子:
make
,选择create
,generate
,build
等词汇会更有表现力,更加专业。find
,选择search
,extract
,recover
等词汇会更有表现力,更加专业。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 Match.sqrt(retval);
}
复制代码
这里的retval表示的是“平方的和”,所以sum_squares
这个词更加贴切你的意图,更加专业。
可是,有些状况下,泛泛的名字也是有意义的,例如一个交换变量的情景:
if (right < left){
tmp = right;
right = left;
left = tmp;
}
复制代码
像上面这种tmp
只是做为一个临时存储的状况下,tmp表达的意思就比较贴切了。所以,像tmp
这个名字,只适用于短时间存在并且特性为临时性的变量。
除了选择一个专业,贴切意图的词汇,咱们也能够经过添加一些先后缀来给这个词附带更多的信息。这里所指的更多的信息有三种:
有些变量是有单位的,在变量名的后面添加其单位可让这个变量名携带更多信息:
duraction
,ducation_secs
携带了更多的信息size
,cache_mb
携带了更多的信息。有些变量是具备一些很是重要的属性,其重要程度是不容许使用者忽略的。例如:
html
,html_utf8
更加清楚地描述了这个变量的格式。password
,plaintext_password
更清楚地描述了这个变量的特色。对于命名,有些既定的格式须要注意:
HomeViewController
。userNameLabel
。product_id
。kConstantName
来表示常量:kCacheDuraction
。SCREEN_WIDTH
。名字越长越难记住,名字越短所持有的信息就越少,如何决定名字的长度呢?这里有几个原则:
若是一个变量做用域很小:则给它取一个很短的名字也无妨。
看下面这个例子:
if(debug){
map <string,int>m;
LookUpNamesNumbers(&m);
Print(m);
}
复制代码
在这里,变量的类型和使用范围一眼可见,读者能够了解这段代码的全部信息,因此即便是取m
这个很是简短的名字,也不影响读者理解做者的意图。
相反的,若是m
是一个全局变量,当你看到下面这段代码就会很头疼,由于你不明确它的类型:
LookUpNamesNumbers(&m);
Print(m);
复制代码
咱们知道驼峰命名能够很清晰地体现变量的含义,可是当驼峰命名中的单元超过了3个以后,就会很影响阅读体验:
userFriendsInfoModel
memoryCacheCalculateTool
是否是看上去很吃力?由于咱们大脑同时能够记住的信息很是有限,尤为是在看代码的时候,这种短时间记忆的局限性是没法让咱们同时记住或者瞬间理解几个具备3~4个单元的变量名的。因此咱们须要在变量名里面去除一些没必要要的单元:
有些单元在变量里面是能够去掉的,例如:
convertToString
能够省略成toString
。
有些缩写是你们熟知的:
doc
能够代替document
str
能够代替string
可是若是你想用BEManager
来代替BackEndManager
就比较不合适了。由于不了解的人几乎是没法猜到这个名称的真正意义的。
因此遇到相似这种状况咱们不能偷懒,该是什么就是什么,不然会起到相反的效果。由于它看起来很是陌生,跟咱们熟知的一些缩写规则相去甚远。
有些名字会引发歧义,例如:
need_password
或者is_authenticated
来代替比较好。一般来讲,给布尔值的变量加上is
,has
,can
,should
这样的词可使布尔值表达的意思更加明确这一节讲了不少关于如何起好一个变量名的方法。其实有一个很简单的原则来判断这个变量名起的是不是好的:那就是:团队的新成员是否能迅速理解这个变量名的含义。若是是,那么这个命名就是成功的;不然就不要偷懒了,起个好名字,对谁都好。其实若是你养成习惯多花几秒钟想出个好名字,渐渐地,你会发现你的“命名能力”会很快提高。
在写程序的过程当中咱们会声明不少变量(成员变量,临时变量),而咱们要知道变量的声明与使用策略是会对代码的可读性形成影响的:
相对的,对于变量的声明与使用,咱们能够从这四个角度来提升代码的可读性:
在一个函数里面可能会声明不少变量,可是有些变量的声明是毫无心义的,好比:
有些变量的声明彻底是画蛇添足,它们的存在反而加大了阅读代码的成本:
let now = datetime.datatime.now()
root_message.last_view_time = now
复制代码
上面这个now
变量的存在是毫无心义的,由于:
datetime.datatime.now
已经很清楚地表达了意思因此彻底不用这个变量也是彻底能够的:
root_message.last_view_time = datetime.datatime.now()
复制代码
有的时候为了达成一个目标,把一件事情分红了两件事情来作,这两件事情中间须要一个变量来传递结果。但每每这件事情不须要分红两件事情来作,这个“中间结果”也就不须要了:
看一个比较常见的需求,一个把数组中的某个值移除的例子:
var remove_value = function (array, value_to_remove){
var index_to_remove = null;
for (var i = 0; i < array.length; i+=1){
if (array[i] === value_to_remove){
index_to_remove = i;
break;
}
}
if (index_to_remove !== null){
array.splice(index_to_remove,1);
}
}
复制代码
这里面把这个事情分红了两件事情来作:
index_to_remove
里面。index_to_remove
之后使用splice
方法删除它。(这段代码是JavaScript代码)这个例子对于变量的命名仍是比较合格的,但实际上这里所使用的中间结果变量是彻底不须要的,整个过程也不须要分两个步骤进行。来看一下如何一步实现这个需求:
var remove_value = function (array, value_to_remove){
for (var i = 0; i < array.length; i+=1){
if (array[i] === value_to_remove){
array.splice(i,1);
return;
}
}
}
复制代码
上面的方法里面,当知道应该删除的元素的序号i
的时候,就直接用它来删除了应该删除的元素并当即返回。
除了减轻了内存和处理器的负担(由于不须要开辟新的内容来存储结果变量以及可能不用彻底走遍整个的for语句),阅读代码的人也会很快领会代码的意图。
因此在写代码的时候,若是能够“速战速决”,就尽可能使用最快,最简洁的方式来实现目的。
变量的做用域越广,就越难追踪它,值也越难控制,因此咱们应该让你的变量对尽可能少的代码可见。
好比类的成员变量就至关于一个“小型局部变量”。若是这个类比较庞大,咱们就会很难追踪它,由于全部方法均可以“隐式”调用它。因此相反地,若是咱们能够把它“降格”为局部变量,就会很容易追踪它的行踪:
//成员变量,比较难追踪
class LargeCass{
string str_;
void Method1(){
str_ = ...;
Method2();
}
void Method2(){
//using str_
}
}
复制代码
降格:
//局部变量,容易追踪
class LargeCass{
void Method1(){
string str = ...;
Method2(str);
}
void Method2(string str){
//using str
}
}
复制代码
因此在设计类的时候若是这个数据(变量)能够经过方法参数来传递,就不要以成员变量来保存它。
在实现一个函数的时候,咱们可能会声明比较多的变量,但这些变量的使用位置却不都是在函数开头。
有一个比较很差的习惯就是不管变量在当前函数的哪一个位置使用,都在一开始(函数的开头)就声明了它们。这样可能致使的问题是:阅读代码的人读到函数后半部分的时候就忘记了这个变量的类型和初始值;并且由于在函数的开头就声明了好几个变量,也对阅读代码的人的大脑形成了负担,由于人的短时间记忆是有限的,特别是记一些暂时还不知道怎么用的东西。
所以,若是在函数内部须要在不一样地方使用几个不一样的变量,建议在真正使用它们以前再声明它。
操做一个变量的地方越多,就越难肯定它的当前值。因此在不少语言里面有其各自的方式让一些变量不可变(是个常量),好比C++里的const
和Java中的final
。
有些表达式比较长,很难让人立刻理解。这时候最好能够将其拆分红更容易的几个小块。能够尝试下面的几个方法:
有些变量会从一个比较长的算式得出,这个表达式可能很难让人看懂。这时候就须要用一个简短的“解释”变量来诠释算式的含义。使用书中的一个例子:
if line.split(':')[0].strip() == "root"
复制代码
其实上面左侧的表达式其实得出的是用户名,咱们能够用username
来替换它:
username = line.split(':')[0].strip()
if username == "root"
复制代码
除了以“变量”替换“算式”,还能够用“变量”来替换含有更多变量更复杂的内容,好比条件语句,这时候该变量能够被称为"总结变量"。使用书中的一个例子:
if(request.user.id == document.owner_id){
//do something
}
复制代码
上面这条判断语句所判断的是:“该文档的全部者是否是该用户”。咱们可使用一个总结性的变量user_owns_document
来替换它:
final boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document){
//do something
}
复制代码
德摩根定理:
not(a or b or c)
等价于(not a) and (not b) and (not c)
not(a and b and c)
等价于(not a) or (not b) or (not c)
当咱们条件语句里面存在外部取反的状况,就可使用德摩根定理来作个转换。使用书中的一个例子:
//使用德摩根定理转换之前
if(!(file_exists && !is_protected)){}
//使用德摩根定理转换之后
if(!file_exists || is_protected){}
复制代码
在读过一些好的源码以后我有一个感觉:好的源码每每都看上去都很漂亮,颇有美感。这里说的漂亮和美感不是指代码的逻辑清晰有条理,而是指感官上的视觉感觉让人感受很舒服。这是从一种纯粹的审美的角度来评价代码的:富有美感的代码让人赏心悦目,也容易让人读懂。
为了让代码更有美感,采起如下实践会颇有帮助:
有些时候,咱们能够利用换行和列对齐来让代码显得更加整齐。
换行比较经常使用在函数或方法的参数比较多的时候。
使用换行:
- (void)requestWithUrl:(NSString*)url
method:(NSString*)method
params:(NSDictionary *)params
success:(SuccessBlock)success
failure:(FailuireBlock)failure{
}
复制代码
不使用换行:
- (void)requestWithUrl:(NSString*)url method:(NSString*)method params:(NSDictionary *)params success:(SuccessBlock)success failure:(FailuireBlock)failure{
}
复制代码
经过比较能够看出,若是不使用换行,就很难一眼看清楚都是用了什么参数,并且代码总体看上去整洁干净了不少。
在声明一组变量的时候,因为每一个变量名的长度不一样,致使了在变量名左侧对齐的状况下,等号以及右侧的内容没有对齐:
NSString *name = userInfo[@"name"];
NSString *sex = userInfo[@"sex"];
NSString *address = userInfo[@"address"];
复制代码
而若是使用了列对齐的方法,让等号以及右侧的部分对齐的方式会使代码看上去更加整洁:
NSString *name = userInfo[@"name"];
NSString *sex = userInfo[@"sex"];
NSString *address = userInfo[@"address"];
复制代码
这两者的区别在条目数比较多以及变量名称长度相差较大的时候会更加明显。
当涉及到相同变量(属性)组合的存取都存在的时候,最好以一个有意义的顺序来排列它们:
举个例子:相同集合里的元素同时出现的时候最好保证每一个元素出现顺序是一致的。除了便于阅读这个好处之外,也有助于能发现漏掉的部分,尤为当元素不少的时候:
//给model赋值
model.name = dict["name"];
model.sex = dict["sex"];
model.address = dict["address"];
...
//拿到model来绘制UI
nameLabel.text = model.name;
sexLabel.text = model.sex;
addressLabel.text = model.address;
复制代码
在写文章的时候,为了能让整个文章看起来结构清晰,咱们一般会把大段文字分红一个个小的段落,让表达相同主旨的语言凑到一块儿,与其余主旨的内容分隔开来。
并且除了让读者明确哪些内容是表达同一主旨以外,把文章分为一个个段落的好处还有便于找到你的阅读“脚印”,便于段落之间的导航;也可让你的阅读具备必定的节奏感。
其实这些道理一样适用于写代码:若是你能够把一个拥有好几个步骤的大段函数,以空行+注释的方法将每个步骤区分开来,那么则会对读者理解该函数的功能有极大的帮助。这样一来,代码既能有必定的美感,也具有了可读性。其实可读性又未尝不是来自于规则,富有美感的代码呢?
BigFunction{
//step1:*****
....
//step2:*****
...
//step3:*****
....
}
复制代码
有些时候,你的某些代码风格可能与大众比较容易接受的风格不太同样。可是若是你在你本身所写的代码各处可以保持你这种独有的风格,也是能够对代码的可读性有积极的帮助的。
好比一个比较经典的代码风格问题:
if(condition){
}
复制代码
or:
if(condition)
{
}
复制代码
对于上面的两种写法,每一个人对条件判断右侧的大括号的位置会有不一样的见解。可是不管你坚持的是哪个,请在你的代码里作到始终如一。由于若是有某几个特例的话,是很是影响代码的阅读体验的。
咱们要知道,一个逻辑清晰的代码也能够由于留白的不规则,格式不对齐,顺序混乱而让人很难读懂,这是十分让人痛心的事情。因此既然你的代码在命名上,逻辑上已经很优秀了,就不妨再费一点功夫把她打扮的漂漂亮亮的吧!
首先引用书中的一句话:
注释的目的是尽可能帮助读者了解得和做者同样多。
在你写代码的时候,在脑海中可能会留下一些代码里面很难体现出来的部分:这些部分在别人读你的代码的时候可能很难体会到。而这些“不对称”的信息就是须要经过以注释的方式来告诉阅读代码的人。
想要写出好的注释,就须要首先知道:
咱们都知道注释占用了代码的空间,并且实际上对程序自己的运行毫无帮助,因此最好保证它是物有所值的。
不幸的是,有一些注释是毫无价值的,它无情的占用了代码间的空间,影响了阅读代码的人的阅读效率,也浪费了写注释的人的时间。这样的注释有如下两种:
//add params1 and params2 and return sum of them
- (int)addParam1:(int)param1 param2:(int)param2
复制代码
上面这个例子举的比较简单,但反映的问题很明显:这里面的注释是彻底不须要的,它的存在反而增长了阅读代码的人的工做量。由于他从方法名就能够立刻意会到这个函数的做用了。
//get information from internet
- (NSString *)getInformation
复制代码
该函数返回的是从网络获取的信息。但这里使用了get前缀,没法看出信息的来源。为了补充信息,使用注释来弥补。但其实这彻底没必要要。只要取一个适当的名字就行了:
- (NSString *)fetchInformation
复制代码
讲完了注释不该该是什么内容,如今讲一下注释应该是什么样的内容:
本书中介绍的注释大概有如下几种:
写代码时的思考
对代码的评价
常量
全局观的概述
你的代码可能不是一蹴而就的,它的产生可能会须要一些思考的过程。然而不少时候代码自己却没法将这些思考表达出来,因此你就可能有必要经过注释的方式来呈现你的思考,让阅读代码的人知道这段代码是哪些思考的结晶,从而也让读者理解了这段代码为何这么写。若是遇到了比你高明的高手,在他看到你的注释以后兴许会立刻设计出一套更加合适的方案。
有些时候你知道你如今写的代码是个临时的方案:它可能确实是解决当前问题的一个方法,可是:
你知道同时它也存在着某些缺陷,甚至是陷阱
你不知道有其余的方案能够替代了
你知道有哪一个方案能够替代可是因为时间的关系或者自身的能力没法实现
也可能你知道你如今实现的这个方案几乎就是“完美的”,由于若是使用了其余的方案,可能会消耗更多的资源等等。
对于上面这些状况,你都有必要写上几个字做为注释来诚实的告诉阅读你的这段代码的人这段代码的状况,好比:
//该方案有一个很容易忽略的陷阱:****
//该方案是存在性能瓶颈,性能瓶颈在其中的**函数中
//该方案的性能可能并非最好的,由于若是使用某某算法的话可能会好不少
复制代码
在定义常量的时候,在其后面最好添加一个关于它是什么或者为何它是这个值的缘由。由于常量一般是不该该被修改的,因此最好把这个常量为何是这个值说明一下:
例如:
image_quality = 0.72 // 最佳的size/quanlity比率
retry_limit = 4 // 服务器性能所容许的请求失败的重试上限
复制代码
对于一个刚加入团队的新人来讲,除了团队文化,代码规范之外,可能最须要了解的是当前被分配到的项目的一些“全局观”的认识:好比组织架构,类与类之间如何交互,数据如何保存,如何流动,以及模块的入口点等等。
有时仅仅添加了几句话,可能就会让新人迅速地了解当前系统或者当前类的结构以及做用,并且这些也一样对开发过当前系统的人员迅速回忆出以前开发的细节有很大帮助。
这些注释能够在一个类的开头(介绍这个类的职责,以及在整个系统中的角色)也能够在一个模块入口处。书中举了一个关于这种注释的例子:
//这个文件包含了一些辅助函数,尾门的文件系统提供了更便利的接口
复制代码
再举一个iOS开发里众所周知的网络框架AFNetworking
的例子。在AFHTTPSessionManager
的头文件里说明了这个类的职责:
//AFHTTPSessionManager` is a subclass of `AFURLSessionManager` with convenience methods for making HTTP requests. When a `baseURL` is provided, requests made with the `GET` / `POST` / et al. convenience methods can be made with relative paths
复制代码
在知道了什么不该该是注释以及什么应该是注释之后,咱们来看一下一个真正合格的注释应该是什么样子的:
注释应当有很高的信息/空间率
也就是说,注释应该用最简短的话来最明确地表达意图。要作到这一点须要作的努力是:
其实好的代码是自解释的,因为其命名的合理以及架构的清晰,几乎不须要注释来向阅读代码的人添加额外的信息,书中有一个公式能够很形象地代表一个好的代码自己的重要性:
好代码 > (坏代码 + 注释)
控制流在编码中占据着很重要的位置,它每每表明着一些核心逻辑和算法。所以,若是咱们可让控制流变得看上去更加“天然”,那么就会对阅读代码的人理解这些逻辑甚至是整个系统提供很大的帮助。
那么都有哪相关实践呢?
写代码也是一个表达的过程,虽然表现形式不一样,可是若是咱们可以采用符合人类天然语言习惯的表达习惯来写代码,对阅读代码的人理解咱们的代码是颇有帮助的。
这里有两个比较典型的情景:
首先比较一下下面两段代码,哪个更容易读懂?
//code 1
if(length > 10)
//code 2
if(10 < length)
复制代码
你们习惯上应该会以为code1容易读懂。
再来看下面一个例子:
//code 3
if(received_number < standard_number)
//code 4
if( standard_number< received_number)
复制代码
仔细看会发现,和上面那一组状况相似,大多数人仍是会以为code3更容易读懂。
那么code1 和 code3有什么共性呢?
它们的共性就是:左侧都是被询问的内容(一般是一个变量);右侧都是用来作比较的内容(一般是一个常量)
这应该是符合天然语言的一个顺序。好比咱们通常会说“今天的气温大于20摄氏度”,而不习惯说“20摄氏度小于今天的气温”。
在判断一些正负逻辑的时候,建议使用if(result)
而不是if(!result)
。
由于大脑比较容易处理正逻辑,好比咱们可能比较习惯说“某某某是个男人”,而不习惯说“某某某不是个女人”。若是咱们使用了负逻辑,大脑还要对它进行取反,至关于多作了一次处理。
在写if/else语句的时候,可能会有不少不一样的互斥状况(好多个elseif
)。那么这些互斥的状况能够遵循哪些顺序呢?
在一个函数或是方法里,可能有一些状况是比较特殊或者极端的,对结果的产生影响很大(甚至是终止继续进行)。若是存在这些状况,咱们应该把他们写在前面,用return来提早返回(或者返回须要返回的返回值)。
这样作的好处是能够减小if/else语句的嵌套,也能够明确体现出:“哪些状况是引发异常的”。
再举一个JSONModel
里的例子,在initWithDictionary:error
方法里面就有不少return操做,它们都体现出了“在什么状况下是不能成功将字典转化为model对象”的;并且在方法的最后返回了对象,说明若是到了这一步,则在转化的过程当中经过了层层考验:
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{
//check for nil input
if (!dict) {
if (err) *err = [JSONModelError errorInputIsNil];
return nil;
}
//invalid input, just create empty instance
if (![dict isKindOfClass:[NSDictionary class]]) {
if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
return nil;
}
//create a class instance
self = [self init];
if (!self) {
//super init didn't succeed
if (err) *err = [JSONModelError errorModelIsInvalid];
return nil;
}
//check incoming data structure
if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
return nil;
}
//import the data from a dictionary
if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
return nil;
}
//run any custom model validation
if (![self validate:err]) {
return nil;
}
//model is valid! yay!
return self;
}
复制代码
关于代码组织的改进,做者介绍了如下三种方法:
一个函数里面每每包含了其主逻辑与子逻辑,咱们应该积极地发现并抽取出与主逻辑不相关的子逻辑。具体思考的步骤是:
好比某个函数的目标是为了寻找距离某个商家最近的地铁口,那么这其中必定会重复出现一些计算两组经纬度之间距离的子逻辑。可是这些子逻辑的具体实现是不该该出如今这个主函数里面的,由于这些细节与这个主函数的目标来说应该是无关的。
便是说,像这种相似于工具方法的函数实际上是脱离于某个具体的需求的:它能够用在其余的主函数中,也能够放在其余的项目里面。好比找到离运动场场最近的几个公交站这个需求等等。
而像这种“抽取子逻辑或工具方法”的作法有什么好处呢?
从函数扩大到项目,其实在一个项目里面,有不少东西不是当前这个项目所专有的,它们是能够用在其余项目中的一些“通用代码”。这些通用代码能够对当前的项目一无所知,能够被用在其余任何项目中去。
咱们能够养成这个习惯,“把通常代码与项目专有代码分开”,并不断扩大咱们的通用代码库来解决更多的通常性问题。
一个比较大的函数或者功能可能由不少任务代码组合而来,在这个时候咱们有必要将他们分为更小的函数来调用它们。
这样作的好处是:咱们能够清晰地看到这个功能是如何一步一步完成的,并且拆分出来的小的函数或许也能够用在其余的地方。
因此若是你遇到了比较难读懂的代码,能够尝试将它所作的全部任务列出来。可能立刻你就会发现这其中有些任务能够转化成单独的函数或者类。而其余的部分能够简单的成为函数中的一个逻辑段落。
在设计一个解决方案以前,若是你可以用天然语言把问题说清楚会对整个设计很是有帮助。由于若是直接从大脑中的想法转化为代码,可能会露掉一些东西。
可是若是你能够将整个问题和想法滴水不漏地说出来,就可能会发现一些以前没有想到的问题。这样能够不断完善你的思路和设计。
这本书从变量的命名到代码的组织来说解了一些让代码的可读性提升的一些实践方法。
其实笔者认为代码的可读性也能够算做是一种沟通能力的一种体现。由于写代码的过程也能够被看作是写代码的人与阅读代码的人的一种沟通,只不过这个沟通是单向的:代码的可读性高,能够说明写代码的人思路清晰,并且TA能够明确,高效地把本身的思考和工做内容以代码的形式表述出来。 因此笔者相信能写出可读性很高的代码的人,TA对于本身的思考和想法的描述能力必定不会不好。
若是你真的打算好好作编程这件事情,建议你从最小的事情上作起:好好为你的变量起个名字。不要再以“我英语很差”或者“没时间想名字”做为托辞;把态度端正起来,平时多动脑,多查字典,多看源码,天然就会了。
若是你连起个好的变量名都懒得查个字典,那你怎么证实你在遇到更难的问题的时候可以以科学的态度解决它? 若是你连编程里这种最小的事情都很差好作,那你又怎么证实你对编程是有追求的呢?
本文已经同步到个人我的博客:传送门
---------------------------- 2018年7月17日更新 ----------------------------
注意注意!!!
笔者在近期开通了我的公众号,主要分享编程,读书笔记,思考类的文章。
由于公众号天天发布的消息数有限制,因此到目前为止尚未将全部过去的精选文章都发布在公众号上,后续会逐步发布的。
并且由于各大博客平台的各类限制,后面还会在公众号上发布一些短小精干,以小见大的干货文章哦~
扫下方的公众号二维码并点击关注,期待与您的共同成长~