`
nathan09
  • 浏览: 144648 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Clean Code 读书笔记

 
阅读更多

笔记:代码整洁之道

命名:



   1、有意义,名副其实:降低代码的模糊度,明确说明代码的用途;

2、避免误导:accountList的类型最好就是List;

3、避免使用多个不同之处较小的名称;

4、避免使用字母l和O,因为它们像数字1和0;

5、做有意义的区分,只有意义不同时才使用不同的名字;

6、废话是无意义的区分,是冗余;

7、使用可搜索的名称:用宏定义数字比直接用数字好,避免使用单字母变量和数字常量;

8、不必使用带类型的匈牙利标记法;

9、避免成员变量使用类名前缀;

10、类名和对象名一般都是名词和名词短语;

11、方法名一般是动词和动词短语;get,set,is前缀;

12、使用解决方案领域内的名称;

13、添加有意义的语境:使用有意义的前缀,创建一个类并将变量声明为成员变量;

14、命名要精确:不要添加无意义的语境;

函数:



1、短小:函数中的缩进层级不应该多于一层或者两层;

2、函数应该做一件事,做好一件事,只做一件事;

3、每个函数只有一个抽象层级,其他的交给下面的抽象层来做;

4、判断函数只做了一件事:不能分函数区段;

5、阅读代码,自顶向下规则:每个函数后面都跟着下一个抽象层的函数;

6、如何让switch只做一件事:通过类工厂创建实际类并返回父类引用,再使用抽象类(父类引用)调用实际类重写的函数;

7、使用描述性的名字;

8、函数参数:为了便于测试,应该少于2个;

9、一元函数的3种形式:

      ①询问关于参数的问题(判断),②转换参数的内容(要有返回值),③参数是个事件(无返回值)

10、如果函数超过2元:应该将其中的某些参数封装成类;

11、函数名字:动/名词形式;

12、避免使用输出参数:如果需要,应该修改所属对象的状态;

13、一个函数要么做一件事(指令),要么回答一件事(询问);

14、使用异常代替返回错误码:这样错误代码可以从主路径代码中分离出来,避免嵌套;

15、分离try/catch块:集中在一个函数中;

注释:



  1、整洁清楚的代码比注释要好得多;

  2、 真正好的注释就是考虑不用写注释;

  3、 需要注释的地方:提供法律信息;解释方法的意图;提供警告信息;

  4、 ToDo注释,提示尚未完成的工作;

  5、 避免括号后面的注释,应当减少嵌套,写成方法;

  6、 删掉被注释掉的代码;

  7、 注释就是一种失败;

格式:



  1、 垂直上的间隔:不同的逻辑之间用空格间隔;

  2、 垂直上的靠近:关系密切的逻辑要靠近才会更加清晰;

  3、 变量在离使用最近的地方声明;

  4、 相关函数:放在一起,调用者放在被调用者的上面;

  5、 概念相关:命名模式相同,应该放在一起;

  6、 水平方向:以不拖动滚动条为准则;

  7、 =,+=等前后的空格可以起强调的作用;

  8、 缩进

  9、 团队规则

对象和数据结构(过程式代码):



  隐藏实现关乎抽象,并不是简单的添加取值器和赋值器;

  1、 对象和数据结构的反对称性:

    过程式代码便于在不改变既有代码的同时,添加新的函数(过程)

    面向对象便于在不改变既有函数的情况下,添加新的类(对象),但是如果抽象类添加新的函数,就需要修改抽象类的所有子类;

  2、 数据结构应该只有公共变量;对象应该只有私有变量和公有函数;

  3、 对象暴露行为,隐藏数据;便于添加新对象类型而无需修改既有行为,同时也难以在既有的对象中添加新行为。

    数据结构暴露数据,没有明显的行为;便于向既有数据结构添加新行为,同时也难以向既有函数添加新数据结构。

错误的处理:



  1、 不要返回null值:这样的话调用者就要处理null,增加工作量;

    解决:抛出异常或者返回特例对象;

  2、 不要传递null值:

  3、 异常的处理:抛出异常或者返回特例对象;如果是调用第三方api可能产生异常,可以新建一个方法或异常类将第三方api打包;

  4、 避免使用可控异常(checked exception):因为处理它们需要修改函数头,违反了开放-闭合原则;应该使用不可控异常(runtime exception),

保持边界整洁:(电子书缺失)



  1、 学习性测试:在项目中引入第三方api的新版本,测试项目是否正常工作;

  2、 处理边界问题方法:用新类包装第三方api;用adapter模式将我们的接口转换为第三方提供的接口;

    更多设计模式:http://blog.csdn.net/qinysong/archive/2006/08/09/1041818.aspx

单元测试:



1、测试驱动开发,整洁的测试有助于进行代码的改动;

  2、整洁测试的标准:可读性;

  3、双重标准:由于运行环境的差异,测试代码和生产代码可以有不同的标准,如效率、内存等;

  4、单个测试的断言数量应该最小化,只测试一个概念;

  5、F.I.R.S.T规则:

    F fast:测试需要频繁运行,因此要能快速运行;

    I Independent:测试应该相互独立;

    R Repeatable:测试应当能在任何环境中重复;

    S Self-validating:自足验证,测试应该能看到成功与否的结果;

    T timely:测试应该及时编写,应该恰好在生产代码之前编写;

类:



  1、类的组织:自顶向下原则,变量列表(公共先于私有,静态先于实体),方法列表(工具方法紧跟在所属方法之后);

  2、类应该短小:类名越明确,类的职责就越清晰;

    (1)每个类单一权责,只有一个修改它的原因,并与少量的其他类协同完成工作;

    (2)内聚:类中含有最少的变量,且每个方法都使用每个变量,此时内聚度最高,类应该是内聚高的;

  3、隔离修改:具体类实现细节,抽象类只呈现概念,利用接口和抽象类可以隔离因为细节的改变而带来的改变类的风险;

系统:



  1、构造与应用代码应该隔离开:就好像建设大楼时,构建大楼的吊车、铲车之类的东西,在大楼投入使用时已经完全不存在一样;软件系统应该讲启动过程和启动过程之后的运行时逻辑分开,在启动过程中创建应用对象,也会存在相互的依赖。

    public Service getService(){

      return new MyServiceIml(...);

    }

    这种延迟化赋值的好处是:在使用对象之前不用关心这种架空构造;坏处是:必须实现这个构造方法,不然无法编译,即使这个对象在运行时永远不会被使用。

  解决方法:

  (1)分解main,将系统中的全部构造过程搬迁到main或者main模块中:main函数创建对象,再将对象传递给应用,应用直接使用;

  (2)工厂,可以让应用控制实体创建的时机;

  (3)依赖注入,控制反转IoC是依赖管理的手段,它将应用需要的依赖对象的创建权责从对象中拿出来,放在一个专注于此事的对象中,并通过依赖注入(赋值器)将依赖对象传递给应用;

  2、扩容

  AOP,面向方面变成。Java中三种方面和类似方面的机制:代理,纯AOP框架,AspectJ

    (1)Java代理:Proxy.newInstance(被代理接口,InvocationHandler h)方法执行后,被代理类的所有方法都会被加上Handler的处理逻辑,这是简单的AOP,但是太复杂;

    (2)纯AOP框架:Spring AOP(需进一步了解)

    (3)AspectJ语言

  最佳的系统架构由模块化的关注面领域组成,每个关注面均用纯Java对象实现。不同的领域之间用最不具有侵害性的方面或类方面工具整合起来。这种架构能测试驱动,就像代码一样。

2011-6-24读到173


====================另一篇==================

[序]

神在细节之中, 细节中自有天地

对于代码, 应无情地重构

代码即设计, 简单代码

[前言]

习艺之要有二, 知和行你应当习得有关原则 模式和实践的知识, 穷尽应知之事, 并且要对其了如指掌, 通过刻苦实践掌握它.

[1 整洁代码]

代码永存

勒布郞(LeBlanc)法则: 稍后等于永不(Later equals never)

保持代码整洁, 对程序员来说是一种态度

什么是整洁的代码?

Bjarne Stroustrup: 优雅, 整洁的代码只做一件事
Grady Booch: 优美的散文, 干净利落的抽象, 直接了当的控制语句
Dave Thomas: 建立在测试之上, 尽量少的代码
Michael Feathers(修改代码的艺术作者): 看起来像是特别在意的人写的
Ron Jeffries: 不要重复, 只做一件事, 小规模抽象
Ward Cunningham(Wiki发明者): 如果每个例程都让你感到深合己意, 那就是整洁代码, 让编程语言看起来像是专门为那个问题而存在, 就可以称之为漂亮的代码

读代码和写代码的时间比例超过10:1, 所以我们要让代码易读

如果每次签入时,代码都比签出时干净, 那么代码就不会腐坏

[2 有意义的命名]

名副其实
避免误导
使用读得出来的名字: 很难拼的,很难读的,不方便交流, 当然拼音也不要用, 因为看不懂
使用可搜索的名字: 方便在代码中查找
避免使用编码: 不要使用匈牙利标记法, 不要成员前缀, 也不要接口前缀I

类名和对象名应该是名词或名词短语
方法名应当是动词或动词短语

每个概念对应一个词(这里get, 别的地方不要retrieve)
别用双关语

尽管用计算机术语, 因为看代码的都是程序员, 所以知道算法, 模式, 数据结构
如果不能做到上一条, 那用所涉领域名称

[3 函数]

第一规则: 短小
第二规则: 还要更短小

只做一件事
每个函数一个抽象层级
向下规则: 代码要有自顶向下的阅读顺序
使用描述性名称, 不要怕名字长

参数最好是0个, 第2好是1个参数, 应避免使用输出参数(采用返回值表达)
需要两三个甚至三个以上的参数, 说明参数可以封装成类了
无副作用(名副其实,不做其他的事)
使用异常替代返回错误码, 并且错误处理就是一件事, 最好独立成一个函数

如何写出这样的函数? 重构!

[4 注释]

注释总是一种失败, 能用代码解释清楚就不要用注释

好的注释, 应该只注释不能用代码表达的信息, 如法律信息(可以提供一个外部文档链接)

[5 格式]

代码格式很重要, 就象女人的外表, 你看一下有兴趣, 才会继续看下去
短文件, 不要太长, 也不要太宽(不要使用水平滚动条就能看到一行的全部)
缩进, 空格, 空行都很重要

[6 对象和数据结构]

过程式代码, 依赖数据结构, 便于在不改动即有数据结构的前提下添加新函数
面向对象代码便于在不改动函数的前提下添加新类

各取所需吧

得墨忒耳定律:
模块不应了解它所操作对象的内部情形

更准确地说,类C的方法f只应该调用以下对象的方法:
1. C
2. 由f创建的对象
3. 作为参数传递给f的对象
4. 由C实体变量持有的对象

方法不应调用由任何函数返回的对象的方法(链式调用 返回原对象, 不受此限制)

[7 错误处理]

Java的checked exception 对于构建健壮的软件并非必需, 因为它违反OCP原则, 如: 修改一个底层函数签名将引起调用链上的一系列修改.

有时, 使用NULL Object模式代替null, 可以整洁代码( NULL Object模式见<<敏捷软件开发>>— 也是Bob大叔写的)

[8 边界]

学习性测试— 写这些测试是为了学习第三方库的使用
定义自己的接口, 隐藏第三方库, 采用adapter模式封装第三方库, 当然这样也可以独立于第三方库测试自己的代码

[9 单元测式]
TDD三定律:

1. 在编写不能通过的单元测试前, 不可编写生产代码
2. 只可编写刚好无法通过的单元测试, 不能编译的也算不通过
3. 只可编写刚好足以通过当前失败测试的生产代码

这三条定律将把你限制在一个很短的时间内(书中说30秒, 但我大概需要2~5分钟, 看来我写测试的粒度不够细)内完成一个循环(写测试->开发->运行测试)

保持测试整洁, 测试的代码和生产的代码同样对待, 他们都是一等公民, 所以也需要认真编写, 要符合整洁代码的规范

[10 类]

类应该短小
类还要更短小

函数是通过代码行数衡量其大小, 类是通过职责任衡量大小

类应该符合单一职责原则(SRP):

我觉得单一职责原则是最重要, 但也是最容易被误解的一个原则

单一职责原则的重点在于变化

以下描述试图用不同的语句说明SRP:

类应该只有一个引起它变化的原因
只能因一个原因去修改类
如果因为这个原因修改了这个类, 而因为另一个原因又要修改这个类, 说明这个类包含太多责任了, 需要重构成两个类

所以类是为了封装变化而存在的, 不是因为问题域中似乎有这样一个对象, 就创建这样一个类, 这是OO新手最容易犯的错

比如为了做一个叫贪吃蛇的游戏, 是不是马上创建出一个Snake类呢? Snake会爬? 还有哪些哪些操作呢? 撞墙? 这时候你可能就很难往下写了, 因为你想当然地以为一定会有Snake这个东西, 其实它未必存在.

[11 系统]

将构造与使用分开: Abstract Factory

DI & IOC

AOP

记录一段非常重要的话:

“一开始就做对系统” 纯属神话. 反之, 我们应该只实现今天的用户故事, 然后重构, 明天再扩展系统 实现新的用户故事
这就是迭代和增量敏捷的精髓的所在.
测试驱动开发 重构 以及它们打造出来的整洁代码, 在代码层面保证了这个过程的实现

(只看到这, 要休息了~~)

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics