十.类
.类的排放,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)
.测试.测试不足;使用覆盖率工具;别略过小测试;被忽略的测试是对不确定事物的疑问;测试边界条件;全面测试相近的缺陷;测试失败的模式有启发性;
测试覆盖率的模式有启发性;测试应该快速;