我的架构思想(三十五):架构是过程,而非结果——架构的表达与逻辑(系统确定性是界面原则的核心)

阅读数:24 2019 年 10 月 12 日 17:05

我的架构思想(三十五):架构是过程,而非结果——架构的表达与逻辑(系统确定性是界面原则的核心)

通过讨论领域间的组成与关系,我们可以尽量将系统的可变性隔离在较晚实现的域中。因此,这意味着先期建设的系统总是不变的、稳定的、可重用的。这是组成论视角对系统架构的主要贡献。但从系统演进的趋势来说,任何系统的组成部分都必然面临我们持续开发行为将会带来的影响。

而界面(interface)——就提出这一概念的本意来说——就是通过对系统确定性加以规格化,从而来避免上述影响21。在我看来,如果一个界面(以及其规格细节)是确切而有效的,那么它应当完全满足如下条件:

21 在 Walter F. Tichy 在 ICSE 1992 上的论文“Programming-in-the-Large: Past, Present, and Future”中提及了层次系统与其界面抽象的出处。其译文“大型程序设计的过去、现在和将来”发表于《计算机科学》1992.06 期,译者陈海东。其原文为:“层次模型中体现的数据抽象原理可追溯到 1966 年 Dennis 和 Van Horn 的论文,它强调了用户和内核间的一个简单接口。Dijkstra 在 1968 年报告了第一个可供使用的、内核分为几层的操作系统。”

  • 准确——合适的知识与表达,至少能让交流双方通过某种形式沟通;
  • 有用——完全明白的意图,至少与系统架构的意图不违背;
  • 可见——执行的效果显而易见,至少在领域或层次上的数据与逻辑流向明确;
  • 可能——应当存在实现的手段,至少可以立即着手开始尝试。

在架构的表达上,由于纵向分开的并列部分之间是没有关系的,因此它们之间也就没有规格化的需求。而横向的层次之间,仅有向下依赖是确定的,因此界面必是由下层来规格化的。这应当包括(上层所需的)数据规格与(调用的)逻辑规格两个部分。

究竟是“上层所需”还是“下层具有”决定了规格的细节呢?这是一个相当关键且又颇具争议的问题。从此前的讨论来看,上层总是(在时序上、相对的)还未能确定的,因此依据“上层所需”来决定规格细节显然是缘木求鱼的事情。但反过来说,如果仅凭“下层具有”来确定规格细节,虽然在逻辑上讲得通顺,却又常常因为对这些规格的细节考虑得不够周全而限制了上层的使用,这又是层次架构在实效性上的疑难。

有两种方法来改善这一问题22。第一种方法是,我们不必过早追求底层界面细节的准确性与可用性,而仅仅将底层所能提供的功能(逻辑)与数据完整(而又粗略地)表达为接口 Int_L0 v0.1。接下来,在后续的开发活动中,上层的开发活动可以基于这些功能与数据进行设计细化,并将这些设计视为对 Intf_L0 _v0.1_ 的封装(例如代理)来实现为 Intf_Ln v1。然后,我们只需要将 Intf_Ln _v1_ 下沉到 L0 层,并以之为公共接口 Intf_L0 _v1_ 发布即可。简单地说,就是在上层细化接口并交由下层发布。尽管这看起来颇为复杂,但确实是实践中常用的方法。

22 界面的设计是自下而上的,这是层次结构的形成和表达等内在性质所决定的,但其便利性则取决于设计者的经验。所以这里必须强调的是,这些仅仅是依据经验来讨论的一些技巧,并且也仅能予这一局面以有限的改善而已。

下面讲第二种方法。参考我们此前的讨论,向下依赖其实只发生于如下两种情况:

  • 上层对下层的数据依赖23

23 逻辑依赖可以通过数据下沉或添加数据抽象层次来变成数据依赖。

  • 当采用嵌套结构时,需要一个向下的注册逻辑。

那么,我们其实是在说:一般层次系统只需要数据界面,而框架 / 引擎类的层次系统只需要多维护一个注册逻辑即可——所以 REST 和 CRUD24事实上成了应付大多数情况的抽象接口方法;即使我们是实现引擎或框架,也只是需要在核心层面考虑清楚注册与回调的机制。

24 REST(Representational State Transfer,表述性状态转移)是一种面向远程服务提供的架构方法,它将架构中“端到端”的关系理解为“资源需求”,并将主要接口抽象为 GET、POST、PUT 和 DELETE 四种方法。而 CRUD 则抽象了面向存储 / 持久层 / 数据的四种基础操作:Create、Retrieve(Query/Select/Read)、Update 与 Delete。

当然,采用第二种方法将意味着上层总是(尽可能多地)在面向数据开发,而非面向既已确定的逻辑,所以这样建立的系统将趋于扁平25。这样的架构在应付系统整体规模与复杂性上的能力其实是不够的。因此采用第二种方法常常也是权宜之计,在一段时间之后,仍然会从上层中抽取确定的逻辑来形成新的层次26

25 这里的意思是说层次过少,尤其是具有确定逻辑的层次过少。

26 平台是从下向上做,还是从上往下做?这个问题的答案其实并不绝对。一般来说,我们能根据经验来确定大体的层次,并在各层的细化中采用“上层实现,下层发布”或“从上层的确定逻辑中抽取层次”的方法。总的来说,这一过程是渐进的,而非一开始就决定的。

但我们如何确定“这是一个界面”或“这些既有的东西可以表达为一个界面”呢?

对于架构中的一条横向的线来说,确定它的界面设计的原则有很多。大致地,则可以分为系统、表现、模块三类原则。其中 **“系统原则”是总的纲要,“模块原则”是系统自身进行(已经进行和计划进行)层次或领域规划时的指导,而“表现原则”** 是界面的风格与样式方面的约束。表 5 列举并分类 Erlang 的一些实践性原则27

27 引自“Sofrware(SW) Engineering Principles”,出自 Erlang 项目的公开文档“Erlang Programming Rules”。

表 5 三类界面原则的示例:Erlang 的一些实践性原则

我的架构思想(三十五):架构是过程,而非结果——架构的表达与逻辑(系统确定性是界面原则的核心)我的架构思想(三十五):架构是过程,而非结果——架构的表达与逻辑(系统确定性是界面原则的核心)

(续表)

我的架构思想(三十五):架构是过程,而非结果——架构的表达与逻辑(系统确定性是界面原则的核心)我的架构思想(三十五):架构是过程,而非结果——架构的表达与逻辑(系统确定性是界面原则的核心)

* 写出朴实无华的代码
** 不要在全局声明和使用私有数据结构,不要暴露对象的实现
***“防御”与否是一个有争议的话题

最后,仅就设计的表达来说28,也可以将一些 GoF 模式作为确定的、确实有效的界面设计参考。一般来说,其结构型模式适合数据层次的规划,而行为型模式适合逻辑层次的规划。后者,尤其是在实现嵌套结构的层次化中相当有效。

28 本书并不详细讨论“架构的设计”或从需求开始的“分析与设计”过程,而是非常严格地区分架构过程设计过程。因此架构表达与设计表达并不是相同的意思,前者的目标是从系统中识别出的基本模型,而后者则是在该模型上的细节刻划,因此后者是可以进一步地借助 GoF 与 UML 这样的工具。注意,尽管 UML 图也分结构型与行为型,但这与我们讨论的内容并不“完全对等”。

评论

发布