持续设计

持续设计

设计它两次

设计软件非常困难,因此您对如何构造模块或系统的初步思考不太可能会产生最佳的设计。如果为每个主要设计决策考虑多个选项,最终将获得更好的结果:设计两次。假设您正在设计用于管理 GUI 文本编辑器文件文本的类。第一步是定义该类将呈现给编辑器其余部分的接口。与其选择想到的第一个想法,不如考虑几种可能性。一种选择是面向行的界面,该界面具有插入,修改和删除整行文本的操作。另一个选择是基于单个字符插入和删除的接口。第三种选择是面向字符串的接口,该接口可对可能跨越线边界的任意范围的字符进行操作。您无需确定每个替代方案的每个功能;在这一点上,勾勒出一些最重要的方法就足够了。

尝试选择彼此根本不同的方法;这样您将学到更多。即使您确定只有一种合理的方法,无论您认为有多糟糕,都应该考虑第二种设计。考虑该设计的弱点并将它们与其他设计的特征进行对比将很有启发性。在对备选方案进行粗略设计之后,列出每个方案的优缺点。接口最重要的考虑因素是高级软件的易用性。在上面的示例中,面向行的界面和面向字符的界面都需要使用文本类的软件中的额外工作。面向行的界面将需要更高级别的软件来在部分行和多行操作(例如剪切和粘贴所选内容)期间拆分和合并行。面向字符的接口将需要循环来实现修改多个字符的操作。还值得考虑其他因素:

  • 一种选择是否具有比另一种更简单的界面?在文本示例中,所有文本界面都相对简单。
  • 一个接口比另一个接口更通用吗?
  • 一个接口是否比另一个接口更有效地实现?在文本示例中,面向字符的方法可能比其他方法慢得多,因为它需要为每个字符单独调用文本模块。

比较了备选设计之后,您将可以更好地确定最佳设计。最佳选择可能是这些选择之一,或者您可能发现可以将多个选择的功能组合到一个比任何原始选择都要好的新设计中。有时,没有其他选择特别有吸引力。发生这种情况时,请查看是否可以提出其他方案。使用您在原始替代方案中发现的问题来推动新设计。如果您在设计文本类并且仅考虑面向行和面向字符的方法,则可能会注意到每个替代方案都比较笨拙,因为它需要更高级别的软件来执行其他文本操作。那是一个危险信号:如果要有一个文本类,它应该处理所有文本操作。为了消除其他文本操作,文本界面需要更紧密地匹配高级软件中发生的操作。这些操作并不总是对应于单个字符或一行。

两次设计原则可以在系统的许多级别上应用。对于模块,您可以首先使用此方法来选择接口,如上所述。然后,您可以在设计实现时再次应用它:对于文本类,您可以考虑实现这些实现,例如行的链接列表,固定大小的字符块或“间隙缓冲区”。实现的目标与接口的目标是不同的:对于实现,最重要的是简单性和性能。在系统的更高层次上探索多种设计也很有用,例如在为用户界面选择功能或将系统分解为主要模块时。在每种情况下,如果您可以比较几种选择,则更容易确定最佳方法。

对其进行两次设计不需要花费很多额外的时间。对于较小的模块(如课程),您可能不需要一两个小时就能考虑替代方法。与您将花费数天或数周时间来实施该课程相比,这是很少的时间。最初的设计实验可能会导致明显更好的设计,这将比花两次设计时间所花的时间多。对于较大的模块,您将花费更多的时间进行初始设计探索,但是实现也将花费更长的时间,并且更好的设计所带来的好处也会更高。

“两次设计”方法不仅可以改善您的设计,而且可以提高您的设计技能。设计和比较多种方法的过程将教您使设计更好或更坏的因素。随着时间的流逝,这将使您更容易排除不良的设计并磨练真正的出色设计。

演进式架构

与传统的前期、重量级的企业架构设计相比,我们建议采用演进式架构(Evolutionary Architecture)。它提供了企业架构的好处,却没有试图准确预测未来所带来的问题。演进式架构不需要猜测组件将如何被重用,而是支持适应性,使用适当的抽象、数据库迁移、测试套件、持续集成和重构来收获系统内发生的重用。系统的驱动技术需求应该尽早确定,以确保在后续的设计和实现中正确处理这些需求。我们主张将决策延迟到最近的负责时刻,事实上,对于一些决策来说,可能是前期的。

敏捷软件开发的一个原则是最后负责时刻的概念。这个概念应用于架构的考虑,在传统架构师中是有争议的。我们认为,只要有适当的原则和适当的测试套件,架构可以不断进化,以满足系统不断变化的需求,允许在不影响系统完整性的情况下,在最后负责的时刻做出架构决策。我们将这种方法称为进化架构,因为我们允许架构随着时间的推移而演变,始终尊重架构指导原则。

演进式架构借用自进化计算而引入的适应度函数,可以客观地展示应用程序及架构是否正在偏离期望的指标,实际上是可以集成到发布流水线中的测试。依赖漂移适应度函数追踪应用程序一个主要指标,即应用依赖的库、API 或环境组件的新鲜度,并可以将过时需要更新的依赖标记出来。随着 Dependabot、Snyk 这类用于检测依赖漂移的工具日趋成熟,我们可以轻松地在软件发布流程中加入依赖漂移适应度函数,以保证应用程序依赖的更新。

下一页