《Clean Architecture》读书笔记

一、前言

软件架构非常重要,架构的主要目的是最小化系统开发和维护的人力成本,提高开发人员的开发效率。

软件设计和软件构架有本质区别,架构更关心软件的高层设计,怎么定义系统和模块的边界,而设计更关心底层的实现细节。

优雅的架构通常不是那么容易就获得的,他需要开发人员去争取、去斗争、去追求。

通常软件有两个维度的要素:功能性和结构性。而大多数时候,国内的研发团队只关心软件的功能性,而忽略结构性。通常这不是开发的错,开发一般话语权不大,被产品和老大推着走。

所以,软件构架不只在于技术,还关乎管理。如果技术管理的老大在团队中不够强硬,那么就容易出问题。在面对产品和PM的时候,不敢据理力争。

按照本书的理论,软件开发过程中的事情分为四个维度:

  • 核心功能(重要且紧急)
  • 软件构架,代码重构(不紧急且重要)
  • 非核心功能(紧急但不重要)
  • 垃圾功能(不紧急也不重要)

因为在国内的研发团队里面,产品可以无限试错,而开发可以无限加班,PM可以无限压榨。所以,作为技术leader需要硬起来,否则无解,不管是大公司还是小公司。
大家都是产品的干系人,产品最后好不好,大家有共同的责任。

另外,在编程方面,leader的眼光和业务能力也很重要。因为他需要及时识别出需求里的变化并采取合理的策略进行代码抽象。

因为架构是需要演进的,代码是会腐烂的,如果一开始的架子搭的不好,腐烂的速度会变得更快。

另外,开发人员太自信也是有害的,动不动就重写并不能解决所有问题。

软件构架不可一促而就,代码结构也是一样,要定期进行重构和代码审查,及时解决代码里面的坏味道。但是如果没有好的开发流程,没有完备的测试,重构和代码审查几乎都是 很难实施的。

二、编程范式

这一节作者讲了主流的三种编程范式,作者强调,这些编程范式并不是增加了开发人员的能力,而是增加了某种约束(或者叫编码自律)。

结构化编程:

在结构化编程出现以前,人们可以自由地使用Goto语句,而结构化编程出现后,有人提出了程序可以只用『顺序』、『选择』和『循环』的组合来完成,Goto语句是有害的。
程序设计采用自顶向下,逐层分解的方式来设计,最后拆分成的子过程就可以完全用上述三种结构来实现。
构结化编程限制了你滥用Goto语句的能力,而没有增加什么新功能。

面向对象编程:

什么是面向对象?有人可能会说是『封装』、『继承』和『多态』。作者认为面向对象的精髓在于多态,它可以优雅地解决软件系统里面的代码依赖问题。
尽量依赖于抽象和接口,而不是依赖于具体实现,这样就可以非常好地划分系统边界。
面向对象的封装限制了你直接访问类的数据和函数的能力。OO限制了你直接控制代码流程的能力,通过多态和接口划分的系统接口,你很难破坏。

函数式编程:

函数式编程限制了你的代码副作用,限制了你直接使用赋值语句的能力。现在面向对象的编程范式已经很普遍了,但是软件的质量和bug数量并没有得到有效控制。
代码副作用随处可见,这些都是导致代码产生各种难以理解的bug的根缘。

三、设计原则(更多关心的是Design)

这一节讲到的设计原则,更多关注的是代码本身。SOLID原则是对设计模式的一个高度概括,熟练掌握并合理运用这些设计原则,可以让你以无招胜有招。

1. 单一职责原则(SRP):

SRP 不单单指函数或类的功能是单一的内聚的。而是说一个模块(类或函数)只有一个引起它进行变化的原因(这里的变化包括添加新feature,改bug)

2. 开闭原则 (OCP)

代码应该对扩展开放,而对修改关闭。当你往系统中添加新功能时,你只需要添加新的代码,而不用修改现有的代码。如果一个模块经常被修改,那么这个模块很可能破坏了开闭原则。

3. 里氏替换原则 (LSP)

里氏替换原则指所有引用其基类的地方必须能够透明地使用其子类而不会破坏系统的功能。此原则可以应用在架构设计的边界定义上面。通过定义一些抽象的基类来
划分系统的边界,使模块进行解耦。

4. 接口隔离原则(ISP)

代码间的依赖应该尽可能依赖接口,而不是具体实现。但凡涉及到具体细节的部分,尽可能设计成不依赖具体实现接口。

5. 依赖倒转原则(DIP)

依赖倒转原则强调模块间的依赖要倒置。上层不要直接依赖下层,而应该依赖抽象。

四、组件原则

当系统足够大以后,需要对大系统进行拆分,并由多个团队负责独立开发。这样的话,不同的团队间只能依赖于定义好的系统边界来进行交互,避免在项目开发过程中任何人都可以对项目的任何部分进行修改。

当然,这种方法不适应于小项目和项目初期。另外面向服务的架构(SOA)也不一定是最好的。

五、常见的架构设计原则

  • 分层架构
  • 分功能架构
  • 洋葱架构
  • 正交形架构
  • 定义子系统的边界
  • 如果有多个类相互依赖,就定义一个外观类,把它们约束起来。只暴露简单清晰的接口和外部产生依赖。presenters and humber object

六、细节的东西需要有抽象

软件架构不要依赖细节,作者提到了以下几种细节:

  • 数据库:各种ORM系统其实就是对细节的一种封装,你的系统在一开始设计的时候,除非客户有明确的需求,否则不要依赖某一个具体的数据库。
  • web:你的系统应该可以独立于Web来设计,当你需要出一款桌面GUI版本软件的时候,你不会太痛苦。
  • UI:UI是最容易变化的,MVC,MVP,各种MVVM框架都是为了解决这个问题。
  • 框架:你的核心业务逻辑应该是与框架无关的,最好不要让某一个框架把你的核心业务逻辑打散了,这样会导致依赖特别强,同时维护成本也会大幅增加。

七、总结

  • 软件架构的目的就是为了解决软件复杂性。架构设计过程中,尽量要把业务逻辑体现在代码逻辑里面。软件构架需要开发人员去争取。
  • 减少代码间的依赖,减少对框架的依赖和对库的依赖,有效识别出哪些是变化的部分,封装这些变化。
  • 再好的架构,如果实施的不好,也会破坏。SOLID原则是对编写Clean Code的指导,一定要每一位系统的开发人员都掌握并敬畏。
  • 架构是需要演化的,一开始定义清楚系统的边界很重要,通过接口隔离和依赖倒置,让模块间的耦合降到最低。

总之,本书是一本非常不错的书,强烈推荐!