《clean architecture》读书笔记
感觉看了本水书,但是看都看了,还是总结一下吧。那就写篇水总结
多年前看过clean code,最近看了名字很像的clean architecture,感觉有碰瓷嫌疑。至少认真看完clean code,再结合一些设计模式的知识,是真的可以实现clean code的。但看完这本clean architecture,离做出好的架构,差距还是有点大。
但书中有些观点还是不错的,有些启发。把印象比较深的几个观点总结一下,附个人的理解。
架构和设计的关系
本书第一章,作者就抛出一个明确的观点,“架构和设计是一回事,没有任何区别”,我觉得这个观点就值得商榷。
作者把“系统”看作粗粒度的代码块组合,所以模块代码组织是“设计”,系统的整体代码组织是“架构”,由此也就把代码设计的SOLID原则延伸到对系统架构也同样适用,不能说毫无道理,但我觉得有点牵强附会。
在我看来,架构和设计确实有共通之处,但是直接等同起来还是太武断了一些。比如说,在整个系统架构里,会涉及到各种中间件的选型,子系统交互,部署运维等,复杂度明显高于模块设计,不能简单地认为是同一件事。例如一个熟练的程序员,可以把单个模块里的类和接口组织得井井有条便于扩展,但这并不代表他一定能胜任复杂大型系统的架构师。
我觉得架构的最主要目的,是使得系统可以低成本地维护,应对后续各种需求变更,最大化程序员的生产力。从这个角度讲,确实跟代码设计的目标有相通之处。
架构设计主要解决3个问题,职责划分、交互、依赖
职责划分,即每个组件(子系统、中间件)有明确单一的职责,减少需求变更需要到处修改的情况
交互,组件之间必定需要交互,因此要设计出一组接口,组件之间仅通过这组接口交互。只要接口保持稳定,组件内部实现的变更就不会影响到其他的组件
依赖,既然组件之间存在交互,那么就必定有依赖关系,包括静态的编译依赖,和动态的运行时依赖。把依赖关系梳理清楚,划分层次,避免牵一发而动全身
其实最终达到的效果,还是组件高内聚低耦合。好吧,必须承认,确实和代码设计很相似。
SOLID
关于这组经典的原则,网上到处都是,就不赘述了。
我个人觉得SRP、ISP、DIP比较有用(至今也没搞懂LSP原则是干什么的)。尤其是DIP,这次看完本书,有了新的理解,所以着重总结一下。
十几年前就有经典的三层结构,把系统自下而上划分为,dao层,service层,controller层,然后上层依赖下层,不允许反向依赖。这个设计使得service依赖了dao,当dao发生变化时,service也要跟着变化。DIP则可以解决这个问题,虽然数据流向是从service层到dao层,但是依赖关系反而是dao依赖service,所以叫“依赖反转”
可以看到,Domain包里放的是领域模型,这是针对业务的建模,所有包都必须依赖它。所以当领域模型发生变化,全局修改是不可避免的。
但是Service依赖的仅仅是自己包内的Repo接口,并没有依赖Dao里的具体实现,相反是Dao依赖Repo接口。所以Dao的修改,对Service没有影响。这就是所谓的“抽象不能依赖具体,具体要依赖抽象”。
业务逻辑层并不在乎数据持久化层的具体实现是什么,用oracle,mysql,或者文件存储来实现,都是Dao层内部实现的事,Dao层是可替换的,只是一个插件。
分层
既然提到了依赖,就多写一段题外话。分层是软件设计的核心思想之一,是管理依赖的关键手段。比如说在windows上开发的应用,如何能比较方便地移植到其他操作系统,尽量多复用一些代码。
我们知道,无论代码如何写,最终都要走到系统调用里,因为只有在内核模式,才能操作文件、内存、IO等。而高级语言没有提供直接发起系统调用的能力。比如在x86_64架构下,要发起getppid()系统调用,需要用如下汇编代码
1 | mov $0x6e %eax |
显然这段代码毫无可移植性,所以操作系统提供了系统调用的包装函数,这使得程序具备在不同硬件架构之间的可移植性。但是如果应用程序直接调用操作系统的包装函数,则无法在操作系统之间移植。所以libc又提供了标准函数库,依赖libc函数来编程,则可以编译出适配不同操作系统的程序,于是进一步在不同的OS之间实现移植。这里也是用到了分层的思想
这里还能体现出分层的另外一个好处,即依赖分层隔离。由于应用程序仅依赖标准库提供的接口,不直接依赖OS API,所以操作系统接口的变化,也就不会直接影响到应用。