目录

《软件设计哲学》 笔记

复杂度

复杂度的三种症状:

  • 修改放大(Change amplification)。需要修改一个地方,却发现改动的点涉及全站,导致难度倍增。
  • 认识负荷(Cognitive load) 。不是越短的代码越简单。有时候一个方法需要更多行的代码反而是简单的,因为它减少了认知负荷。
  • 未知的未知(Unknown unknowns)。存在不明显修改的地方。未知的未知是最糟糕的。

复杂度的原因:

  • 依赖性,模块的相互依赖关系(模块间过度依赖);
  • 模糊性,重要信息模糊不清(例如:变量名、系统文档、代码注释等)。

降低系统复杂性的方法: 抽象和封装。

减少依赖和晦涩的代码,尽可能把复杂性消除,模块之间的依赖要尽量少并且只能依赖抽象和接口,而不能依赖具体的实现。

一些思考

设计

  • 设计两次,第一次设计完成业务需求,第二次设计更好地为今后扩展预留接口。
  • 不同的模块设计目标是不一致的,有的注重扩展性,有的注重性能。
  • 可以考虑注释驱动设计。在扩展系统的时候,可以先编写关键代码的注释,看看能否通过这些注释了解整个新增模块的运行流程。
  • 设计是迭代的,不可能一次做对。另外软件系统也是在不断变化的,设计必须跟着一起进化。商业项目很难在每次实现功能之前都进行重构,时间上不允许。这种可以考虑事后重构,也可以考虑在迭代中间留几天做一些需求评审、测试用例评审、计划安排的事情

Code Review

  • Code Review 需要 Review 的是设计和修改的代码给系统带来的复杂性。
  • 术语、命名、风格等信息尽可能依赖工具来解决
  • 一致性非常重要,如果是遗留系统,尽可能保持跟之间的风格一致比更正确的风格更好。
  • 尽量把一些常规的业务开发模块化和工具化,采用代码生成工具来辅助编写代码会让代码结构更符合设计。

文档

  • 文档需要记录设计迭代过程中的 idea
  • 代码的注释要记录代码中隐藏的信息或者当时实现所做的妥协,需要定期安排人力去清理这些妥协
  • 如果简单的文档不能把一个模块所做的事情说清楚,那么说明这个模块的功能太复杂了。
  • 文档一定要事前写,而不是等到代码写完了再去补充。

工具

  • 工具可以标准化流程、自动化任务,同时还能降低人为引入的错误
  • 尽可能编写工具把复杂的编码活动简单化,利用可视化编程工具来降低系统复杂性
  • 多编写能够自动生成代码的工具,这些工具对降低系统复杂性非常有帮助。

结合平时的开发,个人认为可以先从几件小事做起: 注释、 文档、 命名

注释是抽象的基础,抽象的目的是隐藏复杂性。如果用户必须阅读方法的代码,那么注释可以帮助它了解实体抽象。 可让后来的开发者更快了解功能代码,也可以提醒现有开发人员。

文档可以为开发人员减少认知负担。好的文档可以阐明依赖关系,并且它填补了空白以消除晦涩之处。在迭代过程的重要的想法也一并记录下来,形成版本记录。

好的命名能使得软件设计更容易理解,差的命名更容易产生 Bug。

软件设计的最主要任务就是降低系统复杂性,但是现实世界永远不可能是完美的,消除所有复杂性的代价非常高,如果系统中某些部分固有的复杂性无法被消除(比如遗留系统或者复杂的业务逻辑),那么可以考虑把这些复杂性隐藏起来,或者只让少部分人接触这些复杂性。

如果一个模块频繁被修改,那么此时就应该花费更多的时间来进行设计。如果一个模块从来没人动它,那么应该想办法隐藏这个模块的复杂性。

有时候可以考虑让开发速度稍微慢一点,bug 数量少一点。敏捷开发绝不只是快速开发,还应该包括迭代设计。