前言

每次刚读完一本书的心情总是很好。读完一本好书想的是“哇,这本书写的真不赖,我又收获到好多有趣的知识”。读完一本比较一般的书想的是“这本书写的一般般,让我的脑子休息休息。咦,翻翻豆瓣,书架来找一本更好看的书吧”。 所以现在读完一本书之后,除了一点回味,我又开始想着想去找一本新的书来读了。这其中的原因,可能是对一种“挖宝”一样的心理,期望能看到一些新的东西。当然也不排除用读书来“对正经工作的上事的逃避”,我现在周末的时候越来越不想去想/做工作上的事了,读书倒是一个很好的自我麻痹的借口。 但是这种心理也带来一个问题:就是我读完一本书之后,我都懒得去写一篇读书笔记了。我觉得要真的去讲写读书笔记的意义,未必有什么意义。可能权当是一种记录吧,也是一种训练,完整的叙事,带有逻辑的表达和梳理,能够让自己对刚读的书有更清晰的体感,很多事情只有坚持去做了,才能体会到价值。 如果只是读了一本人文类型的书还好,只是去写写读书的直观感受。但是写技术类的书,就特别的困难,读书时候动过的脑子,仿佛又要动一遍,麻烦的事就像拖着不做-。-。拖着拖着就不想写了。 写这个前言纯粹是一点自我反思,希望自己以后读完书,不要拖延,立马把我的读书笔记写了~~ 还有一点,本来我是想在读书的过程中去写一些笔记,但是我发现这样的效率很低,各种切换,所以还是在读的过程中在微信读书或者纸质书上写一些笔记,等到结束之后再回国头来泛读一次拾掇成一篇读书笔记可能比较符合我现在的习惯。

《架构整洁之道》这本书篇幅不是很长,我大概花了10h就读完了,但是读的过程中觉得很受启发,内容非常的丰富,作者在架构设计的领域应该是一个非常有经验的人,在书里也被种草了作者的其他两本书《代码整洁之道》和《代码整洁之道: 程序员的职业素养》,好废话不多说,下面开始主体内容。

架构的作用

  • 软件架构的终极目标是,用做小的人力成本来满足构建和维护该系统的需求。
  • 架构设计是一门复杂的学问,需要综合考虑编码、质量、部署、发布、运维、排障、升级等各种因素,做出权衡
  • 架构应该不仅仅包含顶层的架构设计,还应该设计所有的底层设计细节,这些细节公共支撑了顶层的架构设计
  • 软件架构师这个职责本身就应该更关注系统的整体结构,而不是具体功能和系统行为的实现。软件架构师必现创建出一个可以让功能实现起来更容易、修改起来更简单、扩展起来更轻松的软件架构。
  • 软件架构师自身需要是程序员,并且必须一直坚持做一线程序员。如果不亲身承受因系统设计而带来的麻烦,就体会不到设计不佳所带来的痛苦,接着就会逐渐迷失正确的设计方向。
  • 如果想设计一个便于推进各项工作的系统,其策略就是要在设计中尽可能长时间地保留尽可能多的可选项。
  • 软件架构设计的主要目标是支撑软件系统的全生命周期,设计良好的架构可以让系统便于理解、易于修改、方便维护,并且能轻松部署。软件架构的终极目标就是最大化程序员的生产力,同时最小化系统的总运营成本。
  • 所有的软件系统都可以降解为策略与细节这两种主要元素

    核心准则

    编程范式

    image.png
    这几个编程范式都是从某个方面限制和规范了程序员的能力。没有一个范式是增加新的能力的。每个编程范式的主要是为了告诉我们不能做什么,而不是可以做什么。这三个编程范式分别限制了goto语句,函数指针和赋值语句。作者还从更高的维度提到了编程范式和软件架构的关系:

多态是跨越架构边界的手段,函数式编程是规范和限制数据存放位置与访问权限的手段,结构化编程是各模块的算法实现基础。这和软件架构的三大关注点不谋而合:功能性,组件独立和数据管理

在面向对象中多态函数的特性实际上是函数指针的应用,但是函数指针显式转化类型存在的问题是,这是依赖人为约定的,而面向对象中消除了人工遵守这些约定的必要,也就消除了这方面的危险性。因此面向对象编程实际上是对程序间接控制权的转移进行了控制。多态可以帮助我们去实现插件式的架构,例如早期操作系统的IO代码中 程序与IO设备无关是其设计原则,不同的io设备只是一个插件。多态还可以帮助我们实现依赖反转,通过接口将具体细节实现和控制流反转。

SOLID设计原则

书中还详细介绍了几个古老的设计原则(和设计模式还是有一些区别),合在一起缩写就是SOLID原则:
image.png

稳定的抽象层

接口比实现稳定,优秀的架构师会花费大量的精力来设计接口,以减少未来对其的改动。在代码中应该多使用抽象接口,尽量避免使用那些多变的具体实现类。同时,对象的创建过程也应该受到严格限制。会选择用抽象工厂来创建。(因为在创建对象的操作上免不了在源代码层次上依赖对象的具体实现,可以通过工厂方式来解决)其依赖关系如下,Flink JobMaster模块中也有类似的设计,可以分析下。
image.png
利用抽象工厂模式来管理依赖关系

组件聚合原则

image.png
image.png
三个原则互相制衡的关系。一般来说,一个软件项目的重心会从该三角区域的右侧开始,先期主要牺牲的是复用性。然后,随着项目逐渐成熟,其他项目会逐渐开始对其产生依赖,项目重心就会逐渐向该三角区域的左侧滑动。换句话说,一个项目在组件结构设计上的重心是根据该项目的开发时间和成熟度不断变动的,我们对组件结构的安排主要与项目开发的进度和它被使用的方式有关,与项目本身功能的关系其实很小。

组件耦合

image.png

稳定依赖原则

关于第三点的说法很有意思,并且作者提供了几个客观衡量的方式
首先,如何定义稳定性?作者给稳定性下的定义:稳定指的是”很难移动”所以稳定性应该与变更的工作量有关。

让软件组件难于修改的一个最直接的办法是让很多其他组件依赖于它。带有很多入向依赖关系的组件是非常稳定的,因为它的任何变更都需要应用到所有依赖它的组件上。

这个定义就意味着一个被很多地方使用的模块可以被认为是稳定的,因为改动这样的一个模块的代价比较高。接口是被引用最多的,因此接口也是最稳定的。在没有接口的情况下,没有共同的根,会导致每个独立的类变得不稳定。

稳定性指标

Fan-in:入向依赖,这个指标指代了组件外部类依赖于组件内部类的数量。 Fan-out:出向依赖,这个指标指代了组件内部类依赖于组件外部类的数量。 I:不稳定性,I=Fan-out/(Fan-in+Fan-out)。该指标的范围是[0,1],I=0意味着组件是最稳定的,I=1意味着组件是最不稳定的。

稳定依赖原则(SDP)的要求是让每个组件的I指标都必须大于其所依赖组件的I指标。也就是说,组件结构依赖图中各组件的I指标必须要按其依赖关系方向递减。稳定依赖原则并不要求系统中所有的组件都是稳定的,如果是这样,系统就无法再变更了。实际上,这个准则,是为了决定哪些组件是稳定的,哪些组件是不稳定的,并将不稳定的组件放在结构图的顶层,不要被稳定的组件所依赖。如果稳定的组件要依赖一个不稳定的组件,可以通过DIP依赖反转,之后接口的稳定性就变高了。

稳定抽象原则

一个类的稳定性是我们所需要的,但是稳定带来的变更难度加大是我们不想看到的,那么解决这个问题的办法就是稳定抽象原则。该原则要求稳定的组件同时应该也是抽象的,这样它的稳定性就不会影响到它的扩展性。如果一个组件想成为稳定组件,那么它应该由接口和抽象类组成,以便将来做扩展。

书的最后一节拾遗讲的也很精彩,实际上是讲如何将这些理论原则,通过编程语言提供的机制落到实处。这里就不展开讲了。

总结

自我总结架构中几个比较关键的点:抽象,延迟细节,职责边界,灵活,可选项,业务实体,可测试性,落实细节