Summer Blog

架构整洁之道

本文是对《架构整洁之道》一书的读书笔记。

编程范式

目前有三种编程范式:

  1. 面向过程,即结构化编程。限制了goto使用,程序的运行靠if/else/while/do/for做控制。
  2. 面向对象,特点为封装、继承、多态。利用多态限制用户对函数指针的使用,限制了函数指针的使用,限制和规范了程序控制权的间接转移。
  3. 面向函数,多使用不可变变量,在函数中不对已有数据做改变。限制赋值的使用。

编程原则SOLID

  1. SRP
  2. OCP
  3. LSP
  4. ISP
  5. DIP

组件构建原则

组件和组件之间的关系并不能一开始就设计好,要随着项目的进行不断完善,在不同的原则之间做妥协,做出最适合当时的设计。

组件

组件是软件的部署单元,是整个系统中可以独立完成部署的最小实体,这也意味着它们应该可以被独立的开发。

组件聚合原则

  1. REP - 复用/发布等同原则:软件的复用最小粒度应等同于其发布的最小粒度。从软件设计和架构设计的角度来看,REP 就是说组件中的类和模块必须是彼此紧密相关的。
  2. CCP - 共同闭包原则:我们应该把那些会同时修改,并且为相同目的而修改的类放到同一组建中。这是 SRP 原则在组建层面的阐述。
  3. CRP - 共同复用原则:不要强迫一个组件依赖它不需要的东西。我们应该将经常共同复用的类和模块放在同一个组件中,不是紧密相关的类不应被放在同一个组件中。

这三个原则彼此存在竞争关系。REP 和 CCP 原则是粘合性原则,它们会让组件变大,而 CRP 原则是排除性原则,它会让组件变得尽量小。优秀的架构需要在三者中找到平衡。早期可能会牺牲复用性,看中可维护性和开发效率,随着项目成熟会逐渐分割模块提升可维护性。

组件耦合原则

  1. 无循环依赖:
    1. 组件之间依赖与公开的版本,每个组件根据自己的需要选择是否升级。
    2. 组件之间也最好没有循环依赖,因为这样在事实上将这些循环依赖的组建组成了一个新的更大的组件,因为它们在开发中都必须使用相同的版本。
    3. 打破循环依赖可以通过:应用依赖反转原则;创建新的组件
  2. 稳定依赖原则:依赖关系必须要指向更稳定的方向。一个组件被其他组件依赖的越多,它就应该是越稳定的。
    1. 不稳定性计算: I = Fan-out / (Fan-in + Fan-out),Fan-in = 依赖该组件的组件数量,Fan-out = 该组件依赖其他组件的数量。I 范围是[0,1],0 时最稳定,1 时最不稳定。
    2. 并不是所有组件都是稳定的,我们想要做的是安排设计稳定和不稳定的组件,使稳定组件位于依赖链的下端
  3. 抽象组件:一个组件的抽象化程度应该与它的稳定性保持一致。
    1. 稳定抽象原则(SAP):一方面稳定的组件应该是抽象的,因为这样稳定性就不会影响扩展性;另一方面,不稳定的组件应该包括实现。
    2. 衡量抽象化程度:A(抽象化程度) = Na(组件中类的数量)/Nc(组件中抽象类和接口的数量)
  4. 组件设计主序列:
    1. 痛苦区:非常稳定但也非常具体的组件。比如数据库表结构,它的可变性很糟糕,同时又很具体,但又被非常多的组件依赖;工具库。在这一区域中,如果是多变的组件,则对系统会造成更多的麻烦。
    2. 无用区:很抽象但没有被其他组件依赖。
    3. 主序列线:坐落在主序列线上的组件,不会被追求稳定性而设计的太过抽象,也不会为了避免抽象而设计的太过不稳定。一个优秀的架构应该追求大部分组件落在主序列线的两端。

组件主序列区域

软件架构

什么是软件架构

实质是规划如何将系统切分成组件,并安排好组件之间的排列关系,以及组件间的通信方式。

目的为了在工作中更好的对这些组件进行研发、部署、运行以及维护。终极目标就是最大化程序员的生产力,最小化系统的总运营成本。如果想设计一个便于推进各项工作的系统,其策略就是要在设计中尽可能长时间地保留尽可能多的选项。

软件架构不应该是与框架相关的。良好的架构应该围绕用例展开,这样架构可以在脱离框架、工具以及使用环境的情况下完整地描述用例。

软件架构要关心:开发、部署、运行、维护、保持可选项、设备无关性。

解耦

解耦三个层次:源码、部署(部署单元,如Jar包)、服务。

通常可以将系统的解耦推行到某种一旦有需求就可以随时转换成服务的程度即可,让整个程序尽量长时间地保持单体的结构,以便给未来留下可选项。一个良好的架构应该能允许一个系统从单体结构开始,以单一文件的形式部署,然后逐渐成长为一系列互相独立的部署单元,甚至是独立的服务或者微服务。

边界划分

边界线应该画在那些不相干的事情中间。比 如业务逻辑和数据库访问之间,业务逻辑和GUI之间。

可以将数据库和GUI都看作业务逻辑的插件,这种架构可以称为插件式架构。系统的核心业务逻辑必须和其他组件隔离,保持独立,其他组件要么是可以去掉的,要么是可以替换的。

策略与层次

一条策略距离系统的输入/输出越远,它所属的层次就越高,直接管理输入/输出的策略在系统中是最低层次的。

通过将策略隔离,并让源码中的依赖方向都统一调整为指向高层策略,可以大幅降低系统变更带来的影响。从另一个角度来说,底层次的组件应该成为高层次组件的插件。

依赖关系原则:源码中的依赖关系必须指向同心圆的内存,即由底层次机制指向高层次策略。

名词解释

业务实体是包含了一系列用于操作关键数据的业务逻辑。实体要么直接包含关键业务数据,要么可以很容易的访问这些数据。业务尸体的接口层则是由那些实现关键业务、操作关键数据的函数组成的。

用例本质是关于如何操作一个自动化系统的描述,定义了用户需要提供的输入、用户应该得到的输出信息以及产生输出应采用的步骤。用例描述某种特定应用情景下的业务逻辑,它并非业务实体中包含的关键业务逻辑。

请求和响应模型用例接收、产生的数据结构。不应直接使用业务实体对象的作为请求和响应,虽然它们有很多相同,但随着时间的推移,这两个对象会以不同的原因、速率发生变更。

谦卑(humble)对象模式设计的目的是区分容易测试的行为与难以测试的行为,并将它们隔离。难以测试的部分为谦卑对象,它们应该尽可能的简化。在系统的边界处,都有可能发现谦卑对象模式的存在。因为跨越边界的通信需要简单的数据结构,而边界可以自然地将系统分割成难以测试和容易测试的部分。在边界使用谦卑对象模式可以提高系统的可测试性。

不完全边界构建不完全边界的一种方式是在将系统分割成一系列可以独立编译、独立部署的组件后,再把它们构成一个组件。换句话说,在系统中所有的接口、用于输入/输出的数据格式等每一件事都设置好后,仍选择将它们统一编译和部署为一个组件。另外可以使用策略模式门户模式实现架构的边界。


comments powered by Disqus