第一章 设计与架构究竟是什么
软件架构的终极目标是,用最小的人力成本满足构建和维护该系统的需求.
慢但是稳,是成功的秘诀
程序员们总是用这句话来欺骗自己:我们可以未来再重构代码,产品上线最重要.但是结果大家都知道,产品上线后重构工作就再也没有人提起.所以重构的时机永远不会再有了…
第二章 两个价值维度
架构价值比行为价值更重要
行为价值(业务)
行为价值并不是程序员工作的全部
架构价值
- software = 灵活的产品
- 软件变更的难度应该和变更的范畴成等比关系,而与变更的具体形状无关
艾森豪威尔矩阵
- 我有两种难题:紧急的和重要的,而紧急的难题永远是不重要的,重要的难题永远是不紧急的.
- 重要比紧急更应该被重视
为好的软件架构而持续斗争
- 公司内部的抗争本来就是无止境的
- 如果你是架构师,这项工作就加倍重要
第四章 结构化编程
- 编码的整个过程应该是可推导的
- 功能性降解拆分,将复杂的逻辑分解为一个个小的单元,以函数,分支,循环等的方式最终呈现.
- 结构化编程是对程序控制权的直接转移的限制
第五章 面向对象编程(OOD)
- 面向对象编程是对程序控制权的间接转移的限制
封装
- 类的private和public属性和方法
继承
- 继承的作用是让我们可以在某个作用域内对外部定义的某一组变量与函数进行覆盖.
多态
- 多态是函数指针的应用
- 插件化是多态的一种实践
- 多态是实现依赖反转的一种好的选择
第六章 函数式编程
- 函数式编程中变量是不可变的
- 原子操作只能保证当前线程下的线程安全问题
- 函数式编程是对程序中赋值操作的限制
设计原则(SOLID)
设计目标:
- 使软件可容忍被改动
- 使软件更容易被理解
- 构建可在多个软件系统中复用的组件
第七章 SRP单一职责原则
- 函数设计也需要遵循SRP原则
- 任何一个软件模块都应该有且仅有一个被修改的原因
- 任何一个软件模块都应该只对某一类行为者负责
- Employee类中各行为应该被分解
第八章 OCP开闭原则
- 良好的设计软件应该易于扩展,同时抗拒修改
- 如果A组件不想被B组件上发生的修改所影响,那么就应该让B组件依赖于A组件
- 利用SRP分解功能,利用DIP反转依赖
- 软件系统不应该依赖不直接使用的组件
第九章 LSP里氏替换原则
- 以接口的形式实现函数调用的可替换性
第十章 ISP接口隔离原则
- 利用接口隔离原则隔离掉不需要的依赖关系,因为不需要的依赖会导致不必要的重新编译和重新部署
第十一章 DIP依赖反转原则
- 如果想要设计一个灵活的系统,在源代码层面的依赖关系中就应该多引用抽象类型而非具体实现
- 接口比具体实现更稳定,因为如果修改接口必须修改实现,但是反之则不一定.
- 不要在具体实现类上创建子类,具体实现应该放到子类去做,除非是比较稳定且通用的逻辑.
- 不要覆盖包含具体实现的函数,如果必须这样,请在覆盖方法中先调用父类方法.
- 源代码依赖方向永远是控制流方向的反转
第十二章 组件
- 组件是软件部署的单元,是整个软件系统在部署过程中可以独立完成部署的最小实体.
- 重定位技术和链接器是组件的单独部署成为可能
- 墨菲定律:程序的规模会一直不断地增长下去,直到将有限的编译和链接时间填满为止
- 摩尔定律:硬件的更新周期为18个月
第十三章 组件聚合
复用/发布等同原则(REP)
- 软件复用的最小粒度应等同于其发布的最小粒度
- ERP原则就是指组件中的类与模块必须是彼此紧密相关的,一个组件不能由一组毫无关联的类和模块组成
- 一个组件中包含的类与模块还应该是可以同时发布的
共同闭包原则(CCP)
- 我们应该将那些会同时修改,并且为相同目的而修改的类放到同一个组件中,而将不会同时修改,并且不会为了相同目的而修改的那些类放到不同的组件中
- CCP原则是SRP原则和OCP原则的组件版
共同复用原则(CRP)
- 不要强迫一个组件的用户依赖他们不需要的东西
- 我们希望组件中的所有类是不可拆分的,不应该出现别人只需要依赖它的某几个类而不需要其他类的情况
- CRP的作用不仅是告诉我们应该将哪些类放在一起,更重要的是要告诉我们应该将哪些类分开
- CRP原则实际上是ISP原则的一个普适版,ISP建议我们不要依赖不需要的类,CRP建议我们不要依赖不需要的组件
- 优秀的架构师应该在REP,CCP和CRP原则间找到一个好的平衡点,一个项目的组件结构设计的重心是根据该项目的开发时间和成熟度不断变化的
第十四章 组件耦合
无依赖环原则
- 组件依赖关系图中不应该出现环
- 依赖环会导致组件之间的发布和运行很难稳定下来
- 每周构建是指team每一周专门抽出一天来进行组件的构建调试工作,而其他时间则忽略组件的构建问题
- 消除循环依赖的一个解决办法是将研发项目划分成一些可单独发布的组件,这些组件独立完成构建和发布
- DIP原则打破循环依赖好的选择
自上而下的设计
- 组件结构图是不可能自上而下被设计出来的,它必须随着软件系统的变化而变化和扩张.
- 组件结构图并不是用来描述应用程序功能的,它更像是应用程序在构建性与维护性方面的一张地图
- 组件结构图的一个重要目的是如何隔离频繁的变更
稳定依赖原则
- 依赖关系必须要指向更稳定的方向
- 任何一个我们预期会经常变更的组件都不应该被一个难于修改的组件所依赖
- 稳定性指标的计算方法:出口依赖/(出口依赖+入口依赖). 0最稳定,1最不稳定.
- 组件并不是都需要稳定
稳定抽象原则
- 一个组件的抽象化程度应该与其稳定性保持一致
- 组件抽象化的计算方法:组件中的抽象类和接口/组件中类的个数 0表现没有抽象类 1表示全是抽象类
- 好的组件结构应该在抽象和稳定之间找到一个好的平衡点
第十五章 什么是软件架构
- 软件架构师自身需要是程序员,并且必须坚持一直做一线程序员,绝对不要听从那些说应该让软件架构师从代码中解放出来以专心解决高阶问题的伪建议
- 软件架构的实质就是规划如何将系统切分成组件,并安排好组件之间的排序关系,以及组件之间的通信方式
- 如果想设计一个便于推进各项工作的系统,其策略就是要在设计中尽可能长时间的保留尽可能多的可选项
- 开发,部署,运行和维护是软件架构的主要目标
- 哪些可选项应该保留:它们就是那些无关紧要的细节设计
- 策略是软件中所有的业务规则与操作过程,是系统真正的价值所在,而细节则是程序员们与策略交互的方式.
- 软件架构师的目标是创建一种系统形态,该形态会以策略为最基本的元素,并让细节与策略脱离关系,并允许在具体决策过程中推迟或延迟与细节相关的内容
第十六章 独立性
所谓独立性是指一个良好的软件架构必须支持一下几点:
- 支持系统所有用例的能力
- 独立的开发能力
- 独立的部署能力
再谈解耦模式
- 源码层次解耦,也叫单体结构
- 部署层次解耦
- 服务层次解耦(微服务)
第十七章 划分边界
软件设计本身就是一门划分边界的艺术。架构师追求的目标就是最大限度降低构建和维护一个系统所需要的人力,而一个系统最消耗人力资源的地方,就是系统中存在的耦合,尤其是那些过早做出的,不成熟的决策所导致的耦合(深以为然)!!!
边界线应该画在何处?
边界线应该画在那些不相关的事情之间。比如UI与业务逻辑,UI与数据库,业务逻辑与数据库…
插件式架构
- 组件应该可以用插件的方式集成到其他系统中。
- 插件式的架构保证了组件的变更不会影响系统整体的业务逻辑。
- 插件式架构是SRP原则的具体实现。
第十九章 策略与层次
软件就是策略语句的集合,软件设计的工作重点之一就是将这些策略彼此分离,然后将它们按照变更的方式进行重新分组。其中变更原因、时间和层次相同的策略应该被分到同一个组件中(OCP)。反之,变更原因、时间和层次不同策略则应该分属不用的组件。
架构设计的工作需要将组件重排组合成为一个有向无环图。图中的每一个节点代表的是一个拥有相同层次策略的组件,每一条单向链接都代表了一种组件之间的依赖关系,他们将不同级别的组件链接起来。
在一个设计良好的架构中,依赖关系的方向通常取决于他们所关联的组件层次。依赖关系应该与数据流向脱钩,而与组件所在的层次挂钩。
第二十章 业务逻辑
- 关键业务逻辑,是指系统中真正用来赚钱或者省钱的部分。
- 关键业务数据,是指关键业务逻辑需要处理的数据。
- 物业实体(Entity):关键业务逻辑+关键业务数据。
第二十二章 整洁架构
- 六边形架构(端口与适配器架构)
- DCI架构
- BCE架构
共同的设计目标:按照不同关注点对软件进行切割。即这些架构都会将软件切割成不同的层,至少有一层是只包含该软件的业务逻辑的,而用户接口,系统接口则属于其他层。
整洁架构图:
由内向外分别代表:
- 业务实体
- 用例
- 接口适配器
- 框架和驱动程序
依赖关系规则
- 越靠近中心,其所在的软件层级就越高。外层代表机制,内层代表策略。
- 依赖关系应该是外层依赖内层,低层依赖高层。
- 使用DIP原则控制依赖。
- 夸边界的数据处理,不直接使用业务实体数据对象,也不要违反依赖规则。
第二十三章 展示器和谦卑对象
- 谦卑对象是指系统中难以测试的部分,比如UI。
- 展示器则是可测试的对象,展示器的工作是将需要展示的数据按照规则进行处理,然后给UI层去展示。
第二十四章 不完全边界
构建不完全边界的一种方法就是在将系统分割成一系列可以独立编译、独立部署的组件之后,再把这些组件构建成一个新的组件。
- 策略模式:不直接依赖实例,而是依赖一个由实例提供的抽象策略。
- 门户模式:类似抽象工厂的方式提供可替换的抽象方法。