读书笔记《修改软件的艺术》

来源 《修改软件的艺术》

[TOC]

重点摘要

  • 优秀的开发者首要考虑的是代码的可维护性
  • 软件开发者之所以会过度开发,是因为我们害怕某个功能会以我们没有预料的方式使用
  • 不同层次的抽象可以帮助我们对组件的关系建立起大局观,也可以让我们可以只在需要的时候关注细节
  • 意外耦合是缺乏优质代码特质的一种体现
  • 封装就是:让做什么,隐藏如何做
  • 封装良好的软件是由外而内而不是由内而外设计的
  • 一部分代码对另一部分 “知道” 得越多,则依赖越重,无论显示的还是隐式的
  • 软件被阅读的次数比编写次数多
  • 在写公用的应用程序接口、方法,或者暴露给外部的其他服务,不要在那个方法中放入任何实现。而是将其位委托其他方法

1. 如何做之前先问做什么、为什么做、给谁做

软件开发者需要领域建模,这就是需要我们去学习领域设计的知识;

开发者需要在善于提出没人想过的问题。这就要要去需要在需求评审的时候提出,多问产品为什么需要这样?出发点是什么?

2.编写整洁的代码

内聚

内聚就是每个片段都只关注一件事,一个类应该只有一个目的;

不同层次的抽象可以帮助我们对组件的关系建立起大局观,也可以让我们可以只在需要的时候关注细节。

代码的层次从高往下应该是从抽象 -> 实体

内聚的代码更容易理解和查找 bug, 因为每个实体都只处理自身的事物

松散耦合

松散耦合的代码不直接依赖它所使用的代码,而是通过一个中间层来调用;

意外耦合是缺乏优质代码特质的一种体现

松散耦合的代码让实体之间的副作用更少,而且更容易测试、复用、扩展

封装良好

高质量的代码是封装良好的,它隐藏了实现细节

封装就是:让做什么,隐藏如何做

封装良好的软件是由外而内而不是由内而外设计的。

在用对象来实现系统的时候,让每个对象自身和所处的环境复制,系统中的每个对象都有自己的用途

默认封装,必要时再暴露

面向接口编程,而不是面向实现编程

封装良好的代码有助于我们管理复杂度,让调用者不必关系被调用者的实现细节,所以更容易修改

代码自主

代码自主,是指让它自己管理自己的职责,不应该互相干预。

自主的原则是:

  • 对象应该控制自身的状态
  • 对象之间不应该相互干预

数据和控制逻辑要分开

自主的代码让我们知道行为应该和它所依赖的数据放到一起

没有冗余

软件不应该自我复制,要没有冗余

没有冗余的代码以为这我们可以只在一个地方修复 bug 和进行更改

3.最后实现设计

3.1 阻碍了可修改代码的产生

在实现设计之前,需用明确是什么阻碍了可修改代码的产生

缺乏封装

一部分代码对另一部分 “知道” 得越多,则依赖越重,无论显示的还是隐式的

滥用继承

少用继承,多用组合

僵化的实现

当缺乏抽象的时候,两个或多个行为之间会有太多的共同性,导致冗余和非必要的复杂性,让代码难以使用。僵化的实现难以修改,难以在日后添加新的差异性

内联代码
没有正常阻隔一拉

3.2 编写可持续性代码的注意事项

  • 删除死代码
  • 保持名称更新
    重命名方法和类,以保持“见名知意”的良好名称。随着开发的进行,对事物理解的加深,代码功能也会随之变化。如果代码变化了,随之亦要重新命名来反应代码更新后的行为

  • 集中决策
    将分散大代码集中一处。让决策集中。如果决策需要改变,只会影响一处。
    如果类和方法都非常内聚,业务规则就分散到系统的各个部分,让其难以理解和修改。将各种流程的规则集中化。将业务规则集中到一起。这样就消除了很多冗余,让代码更容易理解和维护

    这个规则是在平时写代码的时候容易被忽略的,需要重视

  • 对所有外部依赖建立并使用抽象

  • 在建领域模型的时候,需要确保建模完整,对这些类组织,让它们有合理的行为和属性

3.3 意图导向编程

意图导向编程是指,将所有公用接口的代码都委托到不同的方法中,就可以消除这些重复工作。这样代码读起来就像一段脚本或一个菜单,因为它保持同样抽象层次。

在写公用的应用程序接口、方法,或者暴露给外部的其他服务,不要在那个方法中放入任何实现。而是将其委托给其他方法。

3.4 降低复杂度

一个条件(if)语句的复杂度是 2, 两个 if 语句就是的复杂度是4, 这种复杂度是指数型增长的。复杂度越高,越容易出现 bug 所以要降低复杂度

降低复杂度的手段一般有多态,将条件语句的创建放在对象的创建阶段而不是使用阶段

以下内容来自极客时间的专栏 《左耳听风》,第 38| 编程范式游记(9)编程的本质

任何算法都会有两个部分,一个是 Logic 部分,这是用来解决实际问题的。另一个是 Control 部分,这是用来决定用什么策略来解决问题。Logic 部分是真正意义上解决问题的算法,而 Control 部分只是影响解决这个这个问题的效率。程序运行的效率问题和程序的逻辑其实是没有关系的。我们认为,如果将 Logic 和 Control 部分有效地分开,那么代码就会变得更容易改进和维护

有效分离 Logic 、Control 和 Data 是写出好程序的关键所在

Logic 是程序复杂度的下限,然后,我们未来控制程序,需要再搞出很多控制代码,于是 Logic + Control 的相互交织成为了最终程序的复杂度

  • 业务逻辑的复杂度决定了代码的复杂度;
  • 控制逻辑的复杂度 + 业务逻辑的复杂度 ==> 程序代码的混乱不堪
  • 绝大多数程序复杂混乱的根本原因:业务逻辑与控制逻辑的耦合

分离 logic 和 control

  • State Machine 状态机

    • 状态定义
    • 状态变迁条件
    • 状态的 action
  • DSL

    • HTML, SQL, Unix Shell Script, AWK, 正则表达式
  • 编程范式
    • 面向对象:委托、策略、桥接、修饰、 IoC/DIP、MVC…
    • 函数式编程:修饰、管道、拼装
    • 逻辑推导式编程: Prolog

编程的本质:

  • Logic 部分是真正有意义的 (What)
  • Control 部分只影响 Logic 部分的效率 (How)

5.重构遗留代码

重构是指在不改变外部行为的前提下对代码的内部结构进行重组和重新包装

怎样重构

1.只要代码运行没有问题,就不要去修改它,能正常运行,就不用重构
2.只有在那些要修改、添加新功能的代码,才进行重构
3.重构的时候要遵循渐进式、小步修改的原则

何时进行重构

  • 当关键代码维护不善的时候
    别去碰遗留代码是明智的,但是关键代码难以理解变成累赘时,就需要进行清理

  • 当唯一理解代码的人没空的时候

  • 当有信息可以揭示更好的设计的时候

  • 当修复bug 的时候

  • 当需要添加新功能的时候

  • 当需要为遗留代码写文档的时候

  • 当重构比重写容易的时候

yxhuang wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客