在通往 Embedded World 2021 的道路上:第 4 集

编者按:在 Embedded World 2021 举办前推出的 5 篇系列博客中,第 1 篇(即第 1 集)介绍了 Embedded World 概况。在第 2 集中,Randall 重温了他的 C 编程语言知识。第 3 集重点讨论了如何使用面向对象编程来降低复杂性。本篇为第 4 集,阐明良好设计的基本衡量指标是能够根据需求变化重新配置,而不必重新实施构件。最后一篇博客(即第 5 集)写于 Randall 在 Embedded World 2021 发表主题演讲之前,对操作系统需要的空间不断扩大提出了质疑,并谈到了系统分解问题。

到目前为止,我介绍了软件如何发展成为今天的模样,即面向对象,并且说明了面向对象与电子技术的相似之处。若采用面向对象的思维框架进行思考,则能为实际事物(即对象)创建替代模型。我还讨论了面向对象编程 (OOP) 的特性以及如何评估此类模型的质量,但我没有谈及如何建立这些模型。这与如何将系统分解为有用的模块和构件有关。这属于设计方法论的范畴,相关著述曾在 1970 年代初期出现,其中的一些经验教训如今又再度浮现。

最常用的系统划分方法当然是按照功能进行。这包括将系统所需的全部功能细分为构件,并指派开发人员实施这些构件。事实证明,这不是设计系统的最佳方法。它有很多陷阱,导致缺乏灵活性,并为开发人员带来了额外的负担。虽然通过功能分解设计的系统可行,但还有更好的方法。的确,另一种方法更难上手,让人摸不着头脑。前期需要花费更多时间,但之后的开发会更加顺畅,并且开发出的系统能够更轻松地适应变化。

David L. Parnas,来源:Wikipedia (https://en.wikipedia.org/wiki/David_Parnas)

来自卡内基-梅隆大学的 David Parnas 于 1971 年撰写了多篇关于系统设计和模块化的论文。这些论文帮助建立了耦合和内聚的概念,这两个概念我曾在之前的博客中提到过。其中一篇论文是“Information Distribution Aspects of Design Methodology”(设计方法论的信息分发方面),撰写于 1971 年 2 月。这是一篇简短的论文,但非常详尽地说明了什么是设计方法论。他说:“做出能消除某些系统结构可能性的决策,这是设计进展的标志。”他表示,在消除了各种可能性之后,就为后续决策确立了依据,他还提供了例子来支持其主张。这些方法论共同促成一个解决方案。

他确定了三个方法:

1.获得“良好”的外部考虑因素

2.缩短项目启动与完成之间的时间间隔

3.获得一个易于变更的系统

方法 1 与“自上向下”方法有关。方法 2 导致在模块化方面做出的选择可能没有考虑对产品最终可用性的全部影响,但能加快开发人员的开发速度。方法 3 确定最不可能变更的因素,结果是最一般的信息得到最先使用。

他认为,自上而下的方法可能导致系统日后很难变更,只是因为决策标准更偏向外部因素,而不是那些影响变更的因素。在论文这一部分的结尾,他表示这些方法中所作决策的顺序不一致,因此不可能同时满足所有这些方法。这一立场在他的下一篇论文中得到了体现,我会加以说明,但首先我想对他的第一篇论文发表自己的评论。

我发现他对优秀程序员的描述很有意思。他说:“一名优秀的程序员会善加利用提供给他的有用信息!”他继续道,优秀的程序员会很聪明,可能会使用未见诸文献的信息,这意味着如果变更一个构件,那么其他构件也需要变更。这就是我在上一篇文章中提到的“共生性”概念,它与方法 3 不一致。这第一篇论文的结论是,信息隐藏是好的做法,这意味着系统设计人员应该像 Parnas 所说的那样,以“确有必要知情”的原则分享设计信息。

Parnas 继而在 1971 年 8 月写了一篇论文,名为“On the Criterion to Be Used in Decomposing Systems into Modules”(论将系统分解为模块的标准)。这篇论文让他声名鹊起,现在似乎有再受追捧的势头。在这篇论文中,他提出了根据变更可能性来设计系统的理由,也就是前一篇论文中的方法 3。

他的目标是提高系统灵活性和可理解性,同时缩短系统总开发时间,他还举例说明了这种方法的成功经验。在信息隐藏的基础上对代码进行模块化处理,就能在变更管理的基础上对系统进行模块化处理。变更局限于较少的模块,这意味着当需求改变时,系统更改和重新配置会更容易。无论使用功能分解还是变更分解,系统都能正常工作,但在保持信息隐藏的基础上确保高内聚性,可以使系统更加灵活、更可理解且开发速度更快。

假设给定结构的一个众所周知的项目存储在内存中。如果从功能上分解系统,则任何或所有模块都可能在其上运行。如果变更了结构,则访问它的所有模块都必须进行变更。但是,如果该结构是由负责提供访问权限的模块管理,则可以变更该结构而不必变更每个需要对其进行访问的模块。这种方法论的关键是设计人员要从一开始就确定可能发生变更的地方。每种方法论都能得到相同的功能,但不同的是模块化。Parnas 相信,基于变更的模块化比基于功能的模块化更能揭示设计决策,从而使系统更易于理解。

基于信息隐藏的模块化并非没有危害。Parnas 指出,以这种方式设计的系统效率可能很低,但他认为,随着系统的增长,隐藏信息会变得越来越重要。我将在下个月的最后一篇文章中讨论其危害。

从最根本上说,为了提高功能的重复使用性,该功能的接口显露的信息必须尽可能少。正如爱因斯坦所说:“凡事都应该尽可能简化,但不能过于简化。”就我们的情况而言,我们需要将“凡事”替换为“接口”。这项任务非常适合嵌入式工程师。这些工程师接受过许多技术的培训,与不懂技术的人相比,他们能胜任任何级别的设计工作。如果工程师的设计要让最多的用户(即消费者)使用,则设计应尽可能简单。

来源:https://rightingsoftware.org/

正如上文所述,Parnas 的观点正再次受到重视。我建议任何想了解更多信息的人去阅读 Parnas 的论文以及 Juval Löwy 的著作“Righting Software”(扶正软件)。

Löwy 称赞了 Parnas 的方法,即是考虑潜在变化的波动性,通过分解系统来构建系统,并将这些潜在变化封装在构件中。然后,将系统所需的行为实现为这些构件之间的相互作用。一个成功的设计是以最小的构件集合满足所有使用场景的设计。这说起来很容易。

Löwy 说,为了开发此集合,应了解核心使用场景。他补充道,核心使用场景的数量通常并不多,比如 1 到 3 个,结果是组件数量一般不到 12 个。12 个元素的组合和相互作用的数量大得惊人。我想到乐器上的 12 个音符,它们可以产生各种不同的乐曲。它们像音符一样有顺序和时间,所以我认为他是正确的。

他继续说,我们今天的使用场景与距今 20 万年前人类的使用场景并无不同。这种使用场景就是生存,通过打猎和采集野果实现生存的架构与现在让软件工程师能够谋生的架构并无二致(即使用相同的组件)。他认为,大象和老鼠具有相同的架构,不同的是其细节设计。这是一个有说服力的论据。

因此,良好设计的基本衡量指标是能够根据需求变化重新配置,而不必重新实施构件。成功的分解满足所有要求:过去、未来、已知和未知的要求。这是很重要的东西,值得研究。

我在上一篇文章中提到,我将描述一个由于我们要让更多人能使用我们开发的功能而产生的问题,但我认为,这是我们为获得更广阔市场所付出的代价。

关于此作者

Image of Randy Restle

Randall Restle 在电子元器件行业从业四十余载,学识渊博、经验丰富。 现处于半退休状态,担任 DigiKey 应用工程副总裁。他曾经领导过多个应用工程、技术人员和管理人员团队,指导他们开发原创、独有的先进技术产品。

其个人擅长数字信号处理、可编程逻辑实现、运动控制改进和软件设计。 他拥有多项专利,横跨多个行业,是 IEEE 的高级会员。 Randall 拥有辛辛那提大学 BSEE、MS 和 MBA 学位。

More posts by Randall Restle
 TechForum

Have questions or comments? Continue the conversation on TechForum, Digi-Key's online community and technical resource.

Visit TechForum