代码整洁之道(读后感)

总论:
5S原则:
1.整理(Seiri):恰当的命名.
2.整顿(Seiton):物皆有其位,而后物尽其位.每段代码都有该在你希望它所在的地方.不在就要重构(?)
3.清楚(Seiso).无用的注释要去掉.(清除垃圾).
4.清洁(Seiketsu):标准化,遵从编码规范.
5.身美(Shitsuke):要执行上述规程,体现在工作上,乐于改进.

一.整洁代码
处理混乱代码态度:勒布朗(LeBlanc)法则:稍后等于永不(Later equals never).

要快则要整洁.

要有代码感,能看懂好坏,才能写好.编写整洁代码的程序员就像是艺术家.

每次Check in ,让代码比上次更好.

什么是整洁代码?
每人的看法和观点都不同,抽取共性及感想如下:
.规范有意义的命名;
.代码意图清晰,无二义性,单一制原则;
.代码即注释,没有无意义的注释;
.无重复代码;
.代码有单元测试及验收测试;(测试驱动思想,只有验证过的代码,能用才能谈优雅)
.尽量减少依赖,模块独立化;
.性能调至最优.
*.(小块代码?用小面多,分类的代码代替大块代码),实际应用中,有些逻辑复杂的代码暂未能做到,尚需体会.

二.有意义的命名
.名符其实,能从名字读到变量,函数的意义及要做的工作.个人喜欢匈牙利命名法,本书作者不喜欢,猜测原因在于我用的是C++,作者一般用JAVA,在C++或相关的ObjC,C中,很多情况,指向命名,并不能立即知道该变量是全局还是什么类型.

.避免误导;
accountList(真的是List类型,还是真的是List);字母l,和1,o和0,尽量小用.

.有意义的区分;
大学教材中最常用的i,j,k,a,b,c,a1,a2,a3之类的变量名,实在是太害人了.起个有意义的名称会好很多,除非是非常简单,如循环体用的i变量外;否则能写得有意义,就应该让它更清晰.

.使用读得出来的名称
尽量不要自造词,最好用英语,或者用约定俗成的缩写.

.使用可搜索的名称
最常应用,常量之类的,在外头定义,减少直接使用数字的情况.因为改起来会很容易漏,易出错.

.类名应该是名词或名词短语,:Customer,Account;
.方法名应该是动词或动词短语,如:SaveFile();GetData();DeleteItem();

.每个概念对应一个词,一词一义
如,同一堆代码中有:controller,manager,driver,DeviceManager和ProtocolController之类的,尽量统一不需人猜测.

.添加有意义的语境
比如:firstName,street,city就创一个Address的类都包在里面,那就一看就知是啥了,而且便于管理.

三.函数
.短小
小而短,作者的建议20行封顶,短得令人发指.

.只做一件事
单一制原则.判断是 否只做一件事,就看它是否只做了该函数名下同一抽象层上的步骤.也就是说按抽像的层数与决定.另一方法:看是否能再拆出一个函数,该函数不是单纯的重新诠释.

.每个函数同一抽象层级,自顶向下读代码
To Do A,Need Do B ,C;
To Do B,Need To Do d;
To Do C,Need To Do e;
A();
B();
D();
C();
E();

.用多态来替代switch
*这个要学学.抽象工厂.

.函数参数
最好无参,然后是一个,两个,三个或以上,要考虑能否精简了,或者是同一类型的东东,需要重构,比如用户信息之类,弄成一个类就行了.原因很明白,参数越多,函数越难用,最重要的是编写对应的测试会更困难.

.分隔指令和询问
也就是说把is和do分离.这个好.

.用异常代替返回错误码,抽离try/catch
*错误码的话,很多类都要导入,修改会引起重新编译及部署,而异常则不需要.

.结构化编程遵循可以,不过不需要迷信;
结构化编程:一个入口一个出口;但是函数足够短小简单,有return,break,continue也没关系.

.好函数是改出来的.
 
四.注释
.尽量用代码来解释,不要有垃圾的注释;;
.接口要用注释.返回值之类的也可以;
.绝对不能写有误的注释,后人看了绝对会骂.
.不要注释代码,直接删除,有SVN留底的;
 
五.格式
.相当于编程规范,团队合作中一定要保持风格的一致.
.类及文件大小的建议,一个文件200-500行左右就够了.
.垂直阅读,相关的代码从上而下能读到,读起来很舒服.
.水平不超过120个字符.
 
六.对象和数据结构(类与结构的使用)
类应封装,关注实现,不关注数据;数据结构则曝露所有的细节供后续操作.
添加新的数据结构,用类多态的形式好,因为只需要添加新类处理,过程式就麻烦,因为要修改现有的所有操作数据的函数.
反之,添加新的操作函数,用过程式方便,因为只需要加一个函数就OK了,类多态的话要修改所有的类.
因此根据需要来选择.

七.错误处理
.用异常代替返回错误码;分离业务逻辑与异常处理逻辑;不要用NULL....

八.边界
处理方法:
.对第三方SDK进行封装,把引用的地方封装好;
.先做DEMO测试,用好后再应用到工程中. 

*九.单元测试
TDD三定律:
1. 在编写不能通过的单元测试前,不可编写生产代码.(先有测试代码)
2. 只可编写刚好无法通过的单元测试,不能编译也算不通过.
3. 只可编写刚好足以通过当前失败测试的生产代码.
这样将你限制在大概30秒一个循环中.

测试应做到:F.I.R.S.T
快速(FAST),独立(InDependent),可重复(Repeatable),自足验证(Self-Validating),及时(Timely).

十.类
.类的排放,C++中还是先方法后变量,public->private.按项目中的编程规范就行.

.类应该短小;
系统应该由许多短小的类而不是少量巨大的类组成,每个小类封装一个权责,只有一个修改原因,并与少数其它类一起协同达成期望的系统行为.
就像是把工具归置在有许多抽屉,每个抽屉中装有定义和标记良好的组件的工具箱,而不是少数几个装满杂物的大抽屉.(说得很好)

.类设计应该是单一权责,遵从开放闭合原则,依赖倒置原则.
开放闭合:http://baike.baidu.com/view/2493421.htm
关于 依赖倒置,简单来说就是类应该依赖抽象,通过继承与多态能实行系统的扩充,而不是加点东东就大改.
经验:增加多一种设备的支持时,由于每种设备的功能基本一致,因此抽取出了接口,新设备只需继承接口,实现相关功能.那么只要在调用那里创建新的类对象就OK了,
对原有系统不影响同时增加了新功能.

十一.系统
.复杂要人命,让产品难以规划,构建和测试.
多高手,面对一个已成为一团糟,出现严重BUG的代码,经验表明,重构比跟踪那不正常的逻辑更快,有的时候重构完毕,把不合理不正常处改造后BUG就为消除.
经验:试过一个UDP接收的小服务器,原开发人员把udp对象以参数传递的形式使用,这很容易因为某些函数的结束,把UDP释放.现象就是UDP接收偶然会不工作,停下来了;
经过2天时间的找BUG,测试, 还是找不到具体在哪里出现问题.最后决定重构,把该udp对象,变为全局化,在单例类中创建,其它地方只是调用,花费时间少于2天,重构完毕后,
BUG消除.


十二.迭进(系统的构建方法)
.简单设计原则:(按优先级排序)
1.运行所有测试;
2.不可重复;
3.表达了程序员的意图;(清晰,整洁)
4.尽可能减少类和方法的数量;

.不可重复
除了明显的代码重复外,也要关注逻辑的重复,消除不必要的变量等,如一般的IsEmpty(),有的会用一个bool来判断,但如果用size()==0来判断的话又省了.
同时即使几行代码的重复,也可以想办法重构的.
目标是"小规模复用",降低系统的复杂性.


十三.并发编程
.线程部分尽量简单,分离非并发代码.

.限制数据的访问,即处理好同步问题.

.使用数据复本避免共享数据.

.理解,生产者-消费者模型,读者-作者模型,宴席哲学家模型,学会解决方法,实际的并发编程问题基本也是这三种.

.保持同步区域微小
把临界区最小化,性能影响才会最少.

.要考虑线程的正确关闭.

.线程代码测试方法
.先使非线程代码工作;编写可调整的线程代码;运行多于处理器数量的线程;

十四.逐步改进
.心得:
1.看代码,先看实际的运行结果,明白程序是干什么的,这样才能走进代码里面,同时看到了测试代码的编写及如何实用.也说明了怎样写代码别人才能容易看明白.
2.代码编写可以先能运行,再重构;类的制作与划分,目的做到开放闭合,当出现破坏该原则的时候,就需要考虑重构.

十五.JUnit内幕
.心得:
代码如何从好变得更好.
条件判断的封装,if的语义比if not更好.
两个有时序关系的函数合在一起,起免其它程序员用错.
命名的修改.使表达更清晰.(index和length的细微区别).


十六.重构SerialData
.JAVA的重构表演,从中看到,JAVA开发,使用JUNIT作单元测试,COVER作覆盖测试,在这个基础上进行代码的重构.
 重构原则:高内聚,抽象层级的划分.
(其它语言,要找找.)

十七.味道和启发
坏的代码味道:
.注释:不恰当的信息,废弃的,冗余的,写得不好的,注释掉的代码;

.环境:系统要多步才能构建;多步才能测试.

.函数:过多的参数;输出参数;布尔参数;没用到的函数;

.一般性问题:
1.源代码中有多种语言;(C系列较少)
2.明显的行为没有实现:比如输入指令为英文字母g,那么用户肯定期望能忽略大小写,这都没有实现就不好了.
3.不正确的边界行为.边界没有处理好,应通过单元测试,把"应该没问题"明确为"肯定没问题".
4.忽视安全.有问题并放过,最后会引起大问题 .
5.重复.
6.抽象层级混乱.细节应该是低层级的,较高层级的在基类.
7.基类依赖于派生类.基类的设计应该源于接口,不应该被实现左右.
8.信息过多.类及函数,接口等应该尽可能精简,编程不是简单的代码堆积.
9.死代码.一直不会起作用的代码,要及早发现及去除.
10.垂直分隔.函数调用应该在其所用的函数下面一点点.而不应该随处摆放.
11.前后不一致.对同类的操作及方法起同类型的名字,不要随便变换.
12.混淆视听.没有用到的构造函数,普通函数,没用的东东都要去掉.
13.人为耦合.把某些公共要用的enum或变量放在某些类内部了.
14.特性依赖.不要多层次的调用,比如A调用B的对象C的函数之类的行为,因为A是不需要知道C的存在的.
15.选择算子参数.也就是一个函数,多种做法,偷懒的行为.
16.晦涩的意图.(也就是命名不好)
17.位置错误的权责.要考虑一下变量及函数应该在的位置,就近原则,同级抽象层之类的.
18.不恰当的静态方法.静态方法不属于所属对象,同时不需要有多态的意义.
19.使用解释性变量.对一些不透明的模块,如网络字节的解析等,多用中间变量,把每步分析,这样看就很明白了.
20.函数名称应该表达其行为.
21.理解算法.函数不只要能工作,还要理解它的算法,知道这样做是正确的才行.
22.把逻辑依赖改为物理依赖.当两个模块有依赖关系时,不能假设,要把它变成物理依赖.如数组大小,缓存区大小,队列长度等,必须明确,否则当超过假定值时就会出错.
23.用多态替代IF/ELSE,Switch/Case.对于给定的选择类型,不应有多于一个Switch,在一个Switch中创建多态对象,替换其余Switch.
24.遵从标准约定.编码规范.
25.用enum代表有意义的数字常量.
26.准确.确保代码足够准确,如变量public,private,int double,null处理等.
27.结构基于约定.良好的结构比只是好命名的实现要好,这也是重构的所在.
28.封装条件.把解释了的条件意图函数化,这样逻辑会更清晰.
29.避免否定性条件.
30.函数只做一件事.
31.掩蔽时序耦合.明确有时序关系的函数,可以通过函数参数及返回值加强时序关系.
32.别随意.不要这样行,那样也行,随便的态度,代码就随便.代码应该在它最应该的地方,和最好的做法.
33.封装边界条件.边界的处理集中一处,不要周围出现.比如length=index+1之类的.
34.函数应该只在一个抽象层级上.
35.在较高层级放置可配置数据.
36.避免传递浏览.(特性依赖?)

.名称.
;采用描述性名称;与抽象层级相同;使用标准命名法;无歧义;较大作用范围用长名称;(避免编码,@@);名称应该说明副作用;(如判断才执行PayIfNecessary)

.测试.测试不足;使用覆盖率工具;别略过小测试;被忽略的测试是对不确定事物的疑问;测试边界条件;全面测试相近的缺陷;测试失败的模式有启发性;
测试覆盖率的模式有启发性;测试应该快速;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值