Skip to content
On this page

📒 精读《架构整洁之道》

豆瓣 | 微信读书

语雀资料-待整理

推荐序一

程序员的三个层次:
  - 普通程序员:能让程序跑起来,正确处理业务流程和对数据进行计算,会写代码
  - 工程师:有“洁癖”、有工匠精神、有修养的程序员。
    - 怎么做:用工程的方法来编写代码,以便让编程开发更为高效和快速。
      - 工程的方法:把编程当成一种设计,把代码模块化,让这些模块可以更容易地交互拼装和组织,让代码排列整齐
    - 解决了什么问题:使用各种各样的手段和技术不断提高代码的易读性、可扩展性、可维护性和重用性。
  - 架构师:高智商、经验足、不怕难,挑战复杂系统的问题,引领着整个行业前行。
    - 解决了什么问题:问题繁殖和进化,解决1个问题引发多个问题;问题的多少和系统的复杂度成指数型正比。

观点(左耳朵耗子):
无论微观代码、宏观架构、三种编程范式、微服务架构,都在解决同一个问题:分离控制和逻辑。
  - 控制:是对程序流转的与业务逻辑无关的代码或系统的控制(如多线程、异步、服务发现、部署、弹性伸缩等)
  - 逻辑:是实实在在的业务逻辑,是解决用户问题的逻辑。控制和逻辑构成了整体的软件复杂度,有效地分离控制和逻辑会让你的系统得到最大的简化。

推荐序二

举例:蓝绿部署:
  - 内容:用代理分离请求,A组为核心功能(程序基本不更新)、B组为外围请求(程序随业务更新)
  - 核心:动静分离
  - 结合工作:冷热分离、动静分离、读写分离...

所谓架构就是“用最小的人力成本来满足构建和维护系统需求”的设计行为。
  - 无论以前的面向对象系统和现在的分布式系统,在这一点上完全一致。
  - 架构设计思想可以穿越时空,无论在过去还是现在同样适用。
  - 从这种古老的视角看问题,往往更能摆脱细节的困扰,把握问题的核心。正如老子“治大国如烹小鲜。”
    - 作者举了面向对象编程、函数式编程、分布式系统、微服务架构来论证此观点

如果又要保证操作原子性又要能精确还原各时刻的状态?
  - 只提供CR操作,而不提供完整的CRUD操作(就像MySQL的binlog那样)。
  - 平时只要追加操作记录即可,各时刻的状态永远通过重放之前的操作记录得出,这样就彻底避免了状态的错乱。

序言

软件架构(architecture)究竟是什么?
软件架构学关注的的一个重点是组织结构(structure)
软件项目是具有递归(recursive)和分形(fractal)特点的,最终都要由一行行的代码组成。
  - 大型软件项目往往是由小的软件组件构成的,这些软件组件又是由更小的软件组件构成的,层层堆叠,无穷无尽。
软件开发比修建物理建筑需要更长、更专注的设计过程,软件架构师应该比建筑架构师更懂架构!
走快的唯一方法是先走好。

前言

历经多应用、多系统的构建过程,作者的领悟:软件架构的规则是相同的!
为什么?作者的结论:软件架构规则和其他变量完全无关。软件架构规则是永恒不变的。
  - 今天的软件与过去的软件本质上仍然是一样的,仍然是顺序结构、分支结构、循环结构的组合。
  - 计算机代码没有变化,软件架构的规则也就一直保持了一致。
    - 软件架构的规则其实就是排列组合代码块的规则。由于这些代码块本质上没有变化,因此排列组合它们的规则也就不会变化。

第1部分 概述

采用好的软件架构可以大大节省软件项目构建与维护的人力成本。
让每次变更都短小简单,易于实施,并且避免缺陷,用最小的成本,最大程度地满足功能性和灵活性的要求。

第1章 设计与架构究竟是什么

架构与设计的区别:二者没有任何区别。一丁点区别都没有!
底层设计细节和高层架构信息是不可分割的。它们组合在一起,共同定义了整个软件系统,缺一不可。

软件架构的终极目标是,用最小的人力成本来满足构建和维护该系统的需求。

举例揭示软件开发核心特点:要想跑得快,先要跑得稳。
扭转开发者观念,逃离过度自信。过度自信只会使得重构设计陷入和原项目一样的困局中。

本书主题:
要想提高自己软件架构的质量,就需要先知道什么是优秀的软件架构。
而为了在系统构建过程中采用好的设计和架构以便减少构建成本,提高生产力,又需要先了解系统架构的各种属性与成本和生产力的关系。

第2章 两个价值维度

对于每个软件系统,我们都可以通过行为和架构两个维度来体现它的实际价值。

行为价值:即软件系统的行为。是最直观的价值维度。
  - 程序员的工作就是让机器按照某种指定方式运转,给系统的使用者创造或者提高利润。
架构价值:软件系统必须保持灵活。体现在"software"的"soft"上,软件系统必须足够软,容易被修改。
  - 好的系统架构设计应该尽可能做到与“形状”无关。(形状指的是需求拼图块)

软件研发人员应该确保自己的系统在这两个维度上的实际价值都能长时间维持在很高的状态。
只关注其中一个维度,还是错误的维度,会导致系统的价值最终趋降为零。

哪个维度更重要:
系统行为,是紧急的,但是并不总是特别重要。
系统架构,是重要的,但是并不总是特别紧急。

软件研发人员的坚持:
有成效的软件研发团队会迎难而上,毫不掩饰地与所有其他的系统相关方进行平等的争吵。
平衡系统架构的重要性与功能的紧急程度这件事,是软件研发人员自己的职责,而非业务团队。

请记住:
如果忽视软件架构的价值,系统将会变得越来越难以维护,终会有一天,系统将会变得再也无法修改。
如果系统变成了这个样子,说明软件开发团队没有和需求方做足够的抗争,没有完成自己应尽的职责。

第2部分 从基础构件开始:编程范式

第3章 编程范式总览

markdown
三大编程范式一句话总结:
- 结构化编程对`程序控制权的直接转移`进行了限制和规范。
- 面向对象编程对`程序控制权的间接转移`进行了限制和规范。
- 函数式编程对`程序中的赋值`进行了限制和规范。

编程范式的目的:
- 每个编程范式的目的都是`设置限制`
- 这三个编程范式分别限制了goto语句、函数指针和赋值语句的使用。

软件架构的三大关注重点:`功能`性、`组件`独立性以及`数据`管理。

第4章 结构化编程

markdown
Dijkstra在研究中发现的问题:
goto语句的某些用法会导致某个模块无法被递归拆分成更小的、可证明的单元,这会导致无法采用分解法来将大型问题进一步拆分成更小的、可证明的部分。
- goto语句:不受限制的直接控制转移语句。
如何避免:
goto语句的实际效果其实和更简单的分支结构if-then-else以及循环结构do-while是一致的。
如果代码中只采用了这两类控制结构,则一定可以将程序分解成更小的、可证明的单元。

Bohm和Jocopini在2年前发现:人们可以用`顺序结构、分支结构、循环结构`这三种结构构造出任何程序。
证明了:我们构建可推导模块所需要的控制结构集与构建所有程序所需的控制结构集的最小集是等同的。
Dijkstra的证明:
- 顺序结构的正确性、分支结构的可推导性可以通过枚举法证明;
- 循环结构中,循环1次的正确性用枚举法证明,循环N次的正确性采用数学归纳法证明。

结构化编程的好处:功能性降解拆分。
结构化编程范式可将模块递归降解拆分为可推导的单元。
如将大型问题拆分为高级函数,高级函数拆分为低级函数,低级函数用结构化编程范式书写。
通过采用这些技巧,程序员可以将大型系统设计拆分成模块和组件,而这些模块和组件最终可以拆分为更小的、可证明的函数。

科学证明法:
科学理论和科学定律的特点:它们可以被`证伪`,但是没有办法被证明。

测试:
一段程序可以由一个测试来证明其错误性,但是却不能被证明是正确的。

结构化编程范式促使我们先将一段程序递归降解为一系列可证明的小函数,然后再编写相关的测试来试图证明这些函数是错误的。
如果这些测试无法证伪这些函数,那么我们就可以认为这些函数是足够正确的,进而推导整个程序是正确的。

结构化编程范式中最有价值的地方:
它赋予了我们创造可证伪程序单元的能力。这就是为什么现代编程语言一般不支持无限制的goto语句。
更重要的是,这也是为什么在架构设计领域,功能性降解拆分仍然是最佳实践之一。

软件开发过程是由证伪驱动的,与科学研究非常相似。无论在哪个层面,从小函数到大组件,模块和服务。
为了达到这个目的,他们需要将类似结构化编程的限制方法(后续章节)应用在更高的层面上。

第5章 面向对象编程

第6章 函数式编程

第3部分 设计原则

SOLID原则:
  SRP单一职责原则:每个软件模块都有且只有一个需要被改变的理由。
  OCP开闭原则:如果软件系统想要更容易被改变,那么其设计就必须允许*新增代码*来修改系统行为,而非只能靠修改原来的代码。
  LSP里式替换原则:如果想用可替换的组件来构建软件系统,那么这些组件就必须遵守同一个约定,以便让这些组件可以相互替换。
  ISP接口隔离原则:软件设计师应该在设计中避免不必要的依赖。
  DIP依赖反转原则:实现底层细节的代码应该依赖高层策略性的代码。
SOLID原则目的:告诉我们如何将数据和函数组织成为类,以及如何将这些类链接起来成为程序。
  T - 构建软件中层结构的主要目标:使软件可容忍被改动。使软件更容易被理解。构建可在多个软件系统中复用的组件。
    T - 中层架构:SOLID原则应该直接紧贴于具体的代码逻辑之上,这些原则是用来帮助我们定义软件架构中的组件和模块的。这些设计原则主要适用于那些进行模块级编程的程序员。
      T - 高级软件架构:组件设计原则(下一章)

第7章 SRP:单一职责原则

第8章 OCP:开闭原则

第9章 LSP:里氏替换原则

第10章 ISP:接口隔离原则

第11章 DIP:依赖反转原则