整洁架构

架构设计的目标

核心目标

一个良好的架构设计应该围绕着用例展开,这样的架构设计可以脱离框架、工具以及使用环境的情况下完整第描述用例。
软件架构师的目标是创建一种系统形态,该形态会以策略为最基本元素,并让细节与策略脱离关系,以允许在具体决策过程中推迟或延迟与细节相关的内容。
总之,良好的架构设计应该只关注用例,并能将它们与其他的周边因素隔离。

主要目标

软件架构设计的主要目标是支撑软件系统的全生命周期,设计良好的架构可以让系统便于理解、易于修改、方便维护,并且能轻松部署,包括一下几个方面:

  • 开发:方便开发团队进行开发
  • 部署:实现一键部署
  • 运行:使开发人员对系统运行过程一目了然
  • 维护:通过将系统切分为组件,并使用稳定的接口将组件隔离,明确添加新功能的方式,降低在修改过程中对系统其他部分造成伤害的可能性
  • 保留可选项

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

终极目标

软件架构的终极目标就是最大化程序员的生产力,同时最小化系统的总运营成本。

整洁架构

设计的目标:按照不同的关注点对软件进行切割,分成不同的层,至少有一层是只包含业务逻辑的,而用户接口、系统接口则属于其他层。

  • [框架与驱动程序] 数据库、设备、Web、用户界面、外部接口
  • [接口适配器] 网关、控制器、展示器
  • [应用级业务逻辑] 用例
  • [系统级业务逻辑] 业务实体

依赖关系规则

源码中的依赖关系必须只指向同心圆的内层,即由低层机制指向高层策略。

换句话说,就是任何属于内层中的代码都不应该牵涉外层圆中的代码,尤其是内层中的代码不应该引用外层中代码所声明的名字,包括函数、类、变量以及一切其他有命名的软件实体。同样的道理,外层中使用的数据格式也不应该被内层中的代码所使用,尤其是当数据格式是由外层框架所生成时。总之,我们不应该让外层中发生的任何变更影响到内层中的代码。

业务逻辑

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

用例并不描述系统与用户之间的接口,它只描述该应用在某些特定情景下的业务逻辑,这些业务逻辑所规范的是用户与业务实体之间的交互方式,它与数据流入/流出系统的方式无关。

用例依赖于业务实体,而业务实体并不依赖于用例。

这些业务逻辑应该保持纯净,不要掺杂用户界面或者所使用的数据库相关的东西。在理想情况下,这部分代表业务逻辑的代码应该是整个系统的核心,其他低层概念的实现应该以插件形式接入系统中。

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

插件式架构

我们需要将系统分割成组件,其中一部分是系统的核心业务逻辑组件,而另一部分则是与核心业务逻辑无关但负责提供必要功能的插件。然后通过对源代码的修改,让这些非核心组件依赖于系统的核心业务逻辑组件。
其实,这也是一种对依赖反转原则(DIP)和稳定抽象原则(SAP)的具体应用,依赖箭头应该由底层具体实现细节指向高层抽象的方向。

设计原则

SRP:单一职责原则
该设计原则是基于康威定律(Conway's Law)的一个推论——一个软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构。这样,每个软件模块都有且只有一个需要被改变的理由。
很多程序员根据SRP这个名字想当然地认为这个原则就是指:每个模块都应该只做一件事。
对于SRP的正确描述应该为:
任何一个软件模块都应该只对某一类行为者负责。

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

LSP:里氏替换原则
该设计原则是Barbara Liskov在1988年提出的一个著名的子类型定义。简单来说,这项原则的意思是如果想用可替换的组件来构建软件系统,那么这些组件就必须遵守同一个约定,以便让这些组件可以相互替换。

ISP:接口隔离原则
这项设计原则主要告诫软件设计师应该在设计中避免不必要的依赖。

DIP:依赖反转原则
稳定的抽象层:

  • 应在代码中多使用抽象接口,尽量避免使用那些多变的具体实现类。
  • 不要在具体实现类上创建衍生类。
  • 不要覆盖(override)包含具体实现的函数。
  • 应避免在代码中写入与任何具体实现相关的名字,或者是其他容易变动的事物的名字。

层次与边界

架构设计的工作常常需要将组件重排组合成为一个有向无环图
在一个设计良好的架构中,依赖关系的方向通常取决于它们所关联的组件层次。一般来说,低层组件被设计为依赖于高层组件

我们对“层次”是严格按照“输入与输出之间的距离”来定义的。

通过将策略隔离,并让源码中的依赖方向都统一调整为指向高层策略,我们可以大幅度降低系统变更所带来的影响。因为一些针对系统低层组件的紧急小修改几乎不会影响系统中更高级更重要的组件。

组件构建原则

组件聚合

REP 复用发布等同原则
软件复用的最小粒度应等同于其发布的最小粒度。

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

这其实是SRP原则在组件层面上的再度阐述。正如SRP原则中提到的“一个类不应该同时存在着多个变更原因”一样,CCP原则也认为一个组件不应该同时存在着多个变更原因。

CRP 共同复用原则
不是紧密相连的类不应该被放在同一个组件里。
不要强迫一个组件的用户依赖他们不需要的东西。
该原则建议我们将经常共同复用的类和模块放在同一个组件中。

组件耦合

自上而下的设计
组件结构图是不可能自上而下被设计出来的。它必须随着软件系统的变化而变化和扩张,而不可能在系统构建的最初就被完美设计出来。

组件依赖关系是必须要随着项目的逻辑设计一起扩张和演进的。

无依赖环原则 ADP
消除循环依赖 -> 有向无环图
将研发项目划分为一些可单独发布的组件,这些组件可以由单人或一组程序员独立完成。发布的时候可以打上版本号,其他团队可以自主决定是否使用新版本。

打破循环依赖的手段:

  1. 应用依赖反转原则 DIP
  2. 创建一个新的组件,让两个组件都依赖于新的组件

稳定依赖原则 SDP
依赖关系必须要指向更稳定的方向。
可以通过增加抽象组件来打破不稳定的依赖关系。

稳定抽象原则 SAP
稳定抽象原则(SAP)为组件的稳定性与它的抽象化程度建立了一种关联。
一方面,该原则要求稳定的组件同时应该是抽象的,这样它的稳定性就不会影响到扩展性。
另一方面,该原则也要求一个不稳定的组件应该包含具体的实现代码,这样它的不稳定性就可以通过具体的代码被轻易修改。

posted @ 2023-07-26 07:45  NewQ  阅读(14)  评论(0编辑  收藏  举报