架构整洁之道

目录

程序员的三个层次:普通程序员(会编写代码,即能让程序跑起来,能正确地处理业务流程和对数据进行计算)、工程师(不仅会编写代码,还会用工程的方法来编写代码,以便让代码开发更为高效和快速。他们把编程当成一种设计,一种工业设计,把代码模块化,让这些模块可以更容易地交互、拼装和组织,让代码排列整齐-阅读维护这些代码像看阅兵式一样舒心畅快)、架构师(问题的多少和系统的复杂度呈正比,而且不仅是线性正比,还可能呈级数正比,此时就越来越难做技术决定。但是,有一些资深的工程师开始站出来挑战这些问题,有的基于业务分析给出平衡的方案,有的开始尝试设计更高级的技术,有的开始设计更灵活的系统,有的则开始简化和轻量化整个系统......这些高智商、经验足、不怕难的工程师们因亮着这个那个行业前行,他们就是架构师)。

架构整洁之道分为三个部分:编程范式(结构化编程,面向对象编程,函数式编程)、设计原则(SOLID)、软件架构。

架构整洁之道可以让你从微观(代码层面)和宏观(架构层面)两个层面对软件设计有一个全面的了解。

架构整洁之道是架构方面的入门级读物。

无论是微观世界的代码,还是宏观层面的架构,无论是三种编程范式还是微服务架构,他们都在解决一个问题-分离控制(对程序流转的与业务无关的代码或系统的控制)和逻辑(业务逻辑,解决用户问题的代码)。控制和逻辑构成了整体的软件复杂度,有效的分离控制和逻辑会让你的系统得到最大的简化。

区别几组词语:简单和简陋,平衡和妥协,迭代和半成品。

所谓架构,就是用最小的人力成本来满足构建和维护系统需求的设计行为。

治大国如烹小鲜。

软件架构学关注的是一个重点是组织架构。不管是讨论组件、类、函数、模块,还是层级、服务以及微观和宏观的的软件开发过程。

软件开发比修理物理建筑需要更长、更专注的设计过程,软件架构师应该比建筑架构师更懂得架构。

一个好的架构,不仅要在某个时刻满足软件用户、开发者和所有者的需求,更要在一段时间内持续满足他们的后续需求。

一个系统的常规变更不应该是成本高昂的,也不应该是需要难以决策的大型设计调整,更不应该需要单独立项来推进。这些常规变更应该可以融入每日的或者每周的系统维护中去完成。

走快的唯一方式是先走好。

良好的软件架构不是一成不变的,而是通过不断打磨和改进才能最终成就。

第一部分 概述

第一章 设计和架构究竟是什么

一个软件架构的优劣,可以用它满足用户需求所需要的成本来衡量。如果该成本很低,并且在整个生命周期内已知能维持这样的低成本,那么,这个系统的设计就是优良的。

无论从短期还是长期来看,胡乱编写代码的工作速度其实要比循规蹈矩(测试驱动开发)慢。

第二章 两个价值维度

对于每个软件系统,我们都可以通过行为和架构两个维度来衡量它的实际价值。软件研发人员应该确保自己的系统在这两个维度的实际价值都能长时间维持在很高的状态。

软件架构师必须创建出一个可以让功能实现更容易、修改起来更简单、扩展起来更轻松的软件架构。

第二部分 从基础构建开始:编程范式

编程范式指的是程序的编写模式,与具体的编程语言关系不大。

第三章 编程范式总览

编程范式有结构化编程(对程序控制权的直接转移进行了规范和限制,限制的是无限制的跳转)、面向对象编程(对程序控制权的间接转移进行了规范和限制,限制的是函数指针)、函数式编程(对程序对程序中的赋值进行了规范和限制,限制的是修改变量的值)。

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

第四章 结构化编程

顺序结构、分支结构和循环结构可以构造出任何程序。

测试的作用是让我们得出某段程序已经能够实现当前目标这一结论。

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

结构化编程的最有价值的地方就是,它赋予了我们创造可证伪程序单元的能力。

在软件架构领域,功能性降解拆分是最佳实践之一。

软件架构师需要定义可以方便地进行证伪的模块、组件以及服务。

第五章 面向对象编程

面向对象编程就是以多态为手段来对源代码中的依赖关系进行控制的能力。这种能力让软件架构师可以构建出某种插件式架构,让高层策略性组件与底层实现性组件相分离,底层组件可以被编译成插件,实现独立于高层组件的开发和部署。

依赖反转通过引入接口的方式来实现控制流和依赖流的不一致。

第六章 函数式编程

一个架构良好的应用程序应该将状态可修改的部分和不需要修改的部分隔离成单独的组件,然后通过合适的机制来保护可变量。

软件架构师应该着力于将大部分的逻辑都归于不可变组件中,可变状态组件的逻辑应该越少越好。

第三部分 设计原则

第七章 SRP:单一职责

任何一个软件模块(函数和数据结构)都应该只对某一类行为者负责。

单一职责原则主要讨论的是函数和类之间的关系-但是它在两个讨论层面上会以不同的形式出现。在组件层面,我们可以将其称为共同闭包原则,在软件架构层面,它则是用户奠定架构边界的变更轴心。

第八章 OCP:开闭原则

一个设计良好的计算机系统应该在不需要修改的前提下就可以轻易被扩展。

OCP是我们进行系统架构设计的主要原则,其主要目标是让系统易于扩展,同时限制其每次被修改所影响的范围。实现方式是通过将系统划分为一些列组件,并且将这些组件间的依赖关系按层次结构进行组织,使得高阶组件不会因低阶组件被修改而受到影响。

第九章 LSP:里氏替换

如果对于每个类型是S的对象o1都存在一个类型为T的对象o2,能使操作T类型的程序P在用o2替换o1时行为保持不变,我们就可以将S成为T的子类型。

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

第十章 ISP:接口隔离

任何层次的软件设计如果依赖了它并不需要的东西,就会带来意料之外的麻烦。

第十一章 DIP:依赖反转

如果想要设计一个灵活的系统,在源代码层次的依赖关系中就应该多引用抽象类型,而非具体实现。

源代码依赖方向永远是控制流方向的反转-这就是DIP被称为依赖反转原则的原因。

面向接口(抽象类)编程。

第四部分 组件构建原则

第十二章 组件

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

组件化的插件式架构已经成为我们习以为常的软件构件形式。

第十三章 组件聚合

组件相关的基本原则:1)REP:复用/发布等同原则;2)CCP:共同闭包原则;3)CRP:共同复用原则。

软件复用的最小粒度应等同与其发布的最小粒度。

我们应该将那些会同时修改,并且为相同目的而修改的类放到同一个组件中,而将不会同时修改,并且不会为了相同目的而修改的那些类放到不同的组件中。

对于大部分应用程序来说,可维护的重要行远远高于可复用性。

如果变更都集中在同一个组件中,我们就只需要重新部署该组件,其他组件则不需要被重新验证、重新部署。

不要强迫一个组件的用户依赖他们不需要的东西。

将共同复用(为了某个可复用的抽象定义被共同复用)的类和模块放到同一个组件中。

不要依赖不需要的东西。

REP和CRP是粘合性原则,会让组件变大,而CRP是排除性原则,会让组件变小。软件架构师的任务就是要在这三个原则中间进行取舍。

第十四章 组件耦合

无依赖环原则。

稳定依赖的原则(依赖关系必须指向更稳定的方向)。

稳定抽象原则(依赖关系必须指向更抽象的方向)。

第五部分 软件架构

第十五章 什么是软件架构

软件架构师自身需要是程序员,并且一直坚持做一线程序员。软件架构师应该是能力最强的一群程序员,他们通常会字啊自身承接编程任务的同时,逐渐引导整个团队向一个能够最大化生产力的系统设计方向前进。

软件系统的架构质量是由它的构建者所决定的,软件架构这项工作的实质就是规划如何将系统拆分成组件,并安排好组件之间的排列关系,以及组件之间相互通信的方式。

设计软件架构的目的,就是为了在工作中更好地对这些组件进行研发、部署、运行以及维护。

如果想设计一个便于推进各项工作的系统,其策略就死要在设计中尽可能长时间地保留尽可能多的可选项。

软件架构设计的主要目标是支撑软件系统的全生命周期,设计良好的架构可以让系统便于理解、易于修改、方便维护,并且能轻松部署。软件架构的终极目标就是最大化程序员的生产力,同时最小化系统的总运营成本。

一键式的轻松部署是设计软件架构的一个目标。

设计良好的系统架构应该可以使开发人员对系统的运行过程一目了然。架构应该起到揭示系统运行过程的作用。具体来说,就是该架构应该将系统中的用例、功能以及该系统的必备行为设置为对开发者可见的一级实体,简化他们对于系统的理解。

系统维护的成本集中在“探秘”和“风险”这两件事上。其中,“探秘”的成本主要来自我们对现有软件系统的挖掘,目的是去定新增功能或被修复问题的最佳位置和最佳方式。而“风险”则是指我们进行上述修改时,总是可能衍生出新的问题。

通过将系统拆分为组件,并使用稳定的接口将组件隔离,我们可以将未来新功能的添加方式明确出来,并大幅度地降低在修改过程中对系统其它部分造成伤害的可能性。

软件有行为价值和架构价值两种价值。这其中的第二种价值又比第一种更重要,因为他正是软件之所以“软”的原因。

软件的灵活性取决于系统的整体状况、组件的布置以及组件之间的连接方式。我们让软件维持“软”行的方法是尽可能长时间地保留尽可能多的可选项。

所有的软件系统都可以降解为策略与细节这两种主要元素。策略体现的是软件中所有的业务规则与操作过程,因此它是系统真正的价值所在。而细节则是指那些让操作该系统的人、其他系统以及程序员们与策略进行交互,但是又不会影响到策略本身的行为。他们包括I/O设备、数据库、Web系统、服务器、框架、交互协议等。

软件架构师的目标是创建一种系统形态,该形态会以策略为最基本的元素,并让那个细节与策略脱离关系,以允许在具体决策过程中推迟或延迟与细节相关的内容。

一个优秀的架构师应该致力于最大化可选项数量。

第十六章 独立性

一个设计良好的软件架构必须支持以下几点:1)系统的用例[关于如何操作一个自动化系统的莫阿似乎是,定义了输入、输出以及产生输出所应该采取的处理步骤](一个系统的架构必须支持其自身的设计意图)和正常运行;2)系统的维护;3)系统的开发;4)系统的部署。

一个设计良好的架构在行为上对系统最重要的作用就是明确和显式地反应系统设计意图的行为,使其在架构层面上可见。

康威定律:任何一个组织在设计系统时,往往都会复制出一个与该组织内沟通结构相同的系统。

一个设计良好的架构应该通过保留可选项的方式,让系统在任何情况下都能方便地作出必要的变更。

一个系统可以被解藕成若干个水平分层- UI界面、应用独有的业务逻辑、领域普适的业务逻辑、数据库等。

如果我们按照变更原因的不同对系统进行解藕,就可以持续地向系统内添加新的用例,而不会影响旧友的用例。如果我们对支持这些用例的UI和数据库也进行了分组,那么每个用例使用的就是不同面向的UI和数据库,因此增加新用例就更不太可能会影响旧有的用例了。

按水平分层和用例解藕一个系统有很种方式,比如源码层次(控制源代码模块之间的依赖关系)、部署层次(控制部署单元之间的依赖关系)和服务层次。

第十七章 划分边界

软件架构设计本身就是门划分边界的艺术。边界的作用是将软件分割成各种元素,以便约束边界两侧之间的依赖关系。

架构师们所追求的目标应该是最大限度地降低构建和维护一个系统所需的人力资源。

一个系统最消耗人力资源的是系统中存在的藕合。

边界线应该会在那些不相关的事情中间。GUI与业务逻辑无关,所以两者时间应该有一条边界线。数据局与GUI无关,这两者之间也应该有一条边界线。数据库又与业务逻辑无关,所以两者之间也应该有一条边界线。

I/O是无关紧要的。

系统的核心业务逻辑必须和其他组件隔离,保持独立,而这些其他组件要么是可以去掉的,要么是有多种实现的。

为了在软件架构中画边界线,我们需要先将系统分割成组件,其中一部分是系统的核心业务逻辑组件,而另一部分则是与核心业务逻辑无关但负责提供必要功能的插件。

第十八章 边界剖析

第十九章 策略和层次

本质上,所有的软件系统都是一组策略语句的结合。计算机程序不过就是一组仔细描述如何将输入转化为输出的策略语句的集合。

在一个设计良好的架构中,依赖关系的方向通常取决于它们所关联的组件层次。一般来说,低层次组件被设计为依赖与高层组件。

第二十章 业务逻辑

业务逻辑就是程序中那些真正用于赚钱或省钱的业务逻辑与过程。

关键业务逻辑和关键业务数据是紧密相关的,所以,他们很适合被放在同一个对象中处理。我们将这种对象成为“业务实体”。

在理想情况下,代啊码业务逻辑的代码应该是整个系统的核心,其他层次概念的实现应该以插件的形式介入系统中。业务逻辑应该是系统中最独立、复用性最高的代码。

第二十一章 尖叫的软件架构

良好的架构设计应该只关注用例,并能将它们与其他的周边元素隔离。

一个系统的架构应该着重于展示系统本身的设计,而并非该系统所使用的框架。

第二十二章 整洁架构

同心圆代代表了软件系统中的不同层次,通常越靠近中心,其所在的软件层次就越高。基本上,外层圆代表的是机制,内层圆代表的是策略。

通过将系统划分层次,并确保这些层次遵守依赖关系规则,就可以构建出一个天生可测试的系统。

第二十三章 展示器和谦卑对象

可以利用谦卑对象模式将GUI的这两类行为拆分成展示器(可测试的对象)与视图(难以测试的对象,要尽量简单)两部分。

强大的可测试性是一个架构的设计是否优秀的显著衡量标准之一。

通过在系统的边界运用谦卑对象模式,可以大幅地提高整个系统的可测试性。

第二十四章 不完全边界

第二十五章 层次和边界

第二十六章 Main组件

第二十七章 服务:宏观与微观

第二十八章 测试边界

第二十九章 整洁的嵌入式架构

第六部分 实现细节

第三十章 数据库只是实现细节

第三十一章 Web是实现细节

第三十二章 应用程序框架是实现细节

第三十三章 案例分析:视频销售网站

第三十四章 拾遗

你可能感兴趣的:(架构整洁之道)