摘录-架构整洁之道

P1: 概述

  • 好的架构 => 降低 项目构建与维护的人力成本
  • 复杂度 => 成本
  • 大部分程序员的误区: 他们的工作是且仅是按照需求文档编写代码, 并且修复任何bug. 这是行为价值, 紧急而不重要; 架构价值, 重要不紧急.

P2: 从基础构件开始: 编程范式

  • 结构化: 模块降解拆分
  • 面向对象: 封装(可见/不可见), 继承, 多台(IoC依赖反转)
  • 函数式: 隔离可变, 不可变

P3: 设计原则

solid原则: 告诉我们如何将数据和函数组织成类, 以及如何将这些类链接起来成为程序

构建中层结构的主要目标:

  • 使软件可容忍被改动
  • 使软件更容易被理解
  • 构建可在多个软件系统中复用的组件

1. SRP 单一职责原则

康威定律的一个推论: 一个软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构. 每个软件模块都有且仅有一个需要被改变的理由

  • 面向底层实现细节: 每个模块都应该只做一件事; 确保一个函数只完成一个功能
  • 任何一个软件模块都应该只对某一类行为者负责

不同行为者所依赖的代码必须要分开; 需要将服务不同行为者的代码进行切分;

2. OCP 开闭原则

如果软件系统想要更容易被改变, 那么其设计就必须允许新增代码来修改系统的行为, 而非只能拷修改原来的代码

设计良好的计算机软件应该易于扩展, 同时抗拒修改; 应该在不需要修改的前提下就可以轻易被扩展

  • 设计原则: 如果A组件不想被B组件上发生的修改所影响, 那么就应该让B组件依赖于A组件
  • 软件系统不应该依赖于其不直接使用的组件

3. LSP 里氏替换原则

如果想用可替换的组件来构建软件系统, 那么这些组件就必须遵循同一个约定, 以便让这些组件可以相互替换

  • LSP 可以且应该被应用于软件架构层面, 因为一旦违背了可替换性, 该系统架构就不得不为此增添大量复杂的应对机制

4. ISP 接口隔离原则

在设计中避免不必要的依赖

在一般情况下, 任何层次的软件设计如果依赖于不需要的东西, 都会是有害的.

5. DIP 依赖反转原则

高层策略性的代码不应该依赖于底层细节的代码. 应该反过来

  • 如果想要设计一个灵活的系统, 在源代码层次的依赖关系就应该多引用抽象类型, 而非具体实现
  • 稳定的抽象层: 接口比实现更稳定

该设计原则具体的编码守则:

  • 应在代码中多使用抽象接口, 尽量避免使用哪些多变的具体实现类. 适用于所有编程语言; 对象的创建过程也应该受到严格限制, 通常会使用抽象工厂模式
  • 不要在具体实现类上创建衍生类.
  • 不要覆盖(override)包含具体实现的函数
  • 应避免在代码中写入任何具体实现相关的名字, 或者其他容易变动的事物的名字

P4: 组件构建原则

  • 组件是软件的部署单元, 是整个软件系统在部署过程中可以独立完成部署的最小实体.

4.1 组件聚合原则

1. REP: 复用/发布等同原则

  • 软件复用的最小粒度应等同于其发布的最小粒度

2. CCP: 共同闭包原则

  • 我们应该将那些会同时修改, 并且为相同目的而修改的类放到同一个组件中, 而将不会同时修改, 并且不会为了相同目的而修改的那些类放到不同的组件中
  • 一个组件不应该同时存在多个变更原因
  • 对于大部分程序来说, 可维护性的重要性要远远高于可复用性
  • 我们应该将变更原因不同的类放入不同的组件中

3. CRP: 共同复用原则

  • 不要强迫一个组件的用户依赖他们不需要的东西
  • 帮助决策类和模块归属于哪一个组件的原则;
  • 将经常共同复用的类和模块放到同一个组件中

4.2 组件耦合

  • 无依赖环原则: 组件依赖关系图中不应该出现环; (也不应该出现反向依赖)
  • 自下而上的设计: 组件结构图是不可能自上而下被设计出来的. 必须随着软件系统的变化而变化和扩张, 而不可能在系统构建的最初就被完美设计出来
  • 稳定依赖原则: 依赖关系必须要指向更稳定的方向. 预期会经常变更的组件都不应该被一个难以修改的组件所依赖, 否则这个多变的组件也将会变得难以被修改. 稳定性指标=出依赖/(出依赖+入依赖)
  • 稳定性依赖原则SDP: 让每个组件的I指标都必须大于其所依赖组件的I指标. 按依赖方向递减.
  • 稳定抽象原则SAP: 一个组件的抽象化程度应该预期稳定性保持i一致.
  • 稳定抽象原则: 为组件的稳定性于它的抽象性程度建立了一种关联. 1) 要求稳定的组件同时应该是抽象的, 这样它的稳定性就不会影响到扩展性. 2)一个不稳定的组件应该包含具体的实现代码, 这样它的不稳定性就可以通过具体的代码被轻易修改. 如果一个组件要成为稳定组件, 那么它就应该由接口和抽象类组成, 以便将来做扩展

  • 抽象与稳定
  • 痛苦区: 稳定, 难以被修改和扩展, 这个组件不是抽象的(典型案例: 数据库表结构schema, 工具型类库)
  • 无用区: 无限抽象, 没有被其他组件依赖, 往往无法被使用;(典型案例: 历史原因前人写的没有实现的抽象类)
  • 避开这两个区域, 让组件落在主序列线

P5: 软件架构

软件架构师自身需要是程序员, 并且必须一直坚持i做一线程序员, 绝对不要听从那些说应该让软件架构师从代码中解放出来以专心解决高阶问题的伪建议

  • 如果不亲身承受因系统设计而带来的麻烦, 就体会不到设计不佳所带来的痛苦, 接着就会逐渐迷失正确的设计方向
  • 如果想设计一个便于推进各项工作的系统, 其策略就是要在设计中尽可能长时间地保留尽可能多的选项
  • 软件架构设计的主要目标是支撑软件系统的全生命周期, 设计良好的架构可以让系统便于理解, 易于修改, 方便维护, 并且能轻松部署. 软件架构的终极目标就是最大化程序员的生产力, 同时最小化系统的总运营成本.

  • 用例: 一个系统的架构必须能支持其自身的设计意图. 非常直观地支持这类应用可能会涉及的所有用例

  • 我们无法预知全部的用例: 好在架构师应该还是之道整个系统的基本设计意图的

  • 软件架构设计本身就是一门划分边界的艺术.

  • 边界的作用就是将软件分隔成各种元素, 一遍约束边界两侧之间的依赖关系

  • 一个系统中最消耗人力资源的是什么? 系统中存在的耦合–尤其是那些过早做出的, 不成熟的决策所导致的耦合. 那些决策与系统业务需求(用例)无关的, 细节性的决策(框架/数据库/web服务器/工具库/依赖注入等)应该是辅助的, 可以被推迟.

  • 一个设计良好的系统架构不应该依赖于这些细节, 而应该尽可能地推迟i这些细节性的决策, 并致力于将这种推迟i所产生的影响降到最低.

  • 软件架构设计的工作重点之一就是: 将策略彼此分离, 然后将它们按照变更的方式进行重新分组. 维度: 变更原因, 时间和层次

  • 业务实体 Entity: 关键业务逻辑和关键业务数据是紧密相关的, 很适合放在同一个对象中处理

  • 业务逻辑: 应该是系统中最独立, 复用性最高的代码

  • 架构设计的核心目标: 一个良好的架构设计应该围绕着用例展开. 尽可能地允许用户推迟和延后应该采用什么框架/数据库/web服务等. 良好的架构设计应该只关注用例, 并能将它们与其他周边的因素隔离.

  • 外层代表的是机制, 内层代表的是策略
  • 依赖关系规则: 源码中的依赖关系必须只指向同心圆的内层, 即由低层机制指向高层策略

P6: 实现细节

  • 数据库/Web/应用程序框架 都是实现细节!

好的架构 how:

  • 对项目有整体的认识
  • 对需求有足够的了解
  • 对未来有所预见
  • 意识到现有系统的问题, 复杂度的来源
  • 知道如何去解决这个问题: 重新设计/重构/优化

books

2753 Words

2019-10-07 08:00 +0800

comments powered by Disqus