创建一个新系统是一个多阶段的过程:概念和设计,体系结构设计,实现,测试,发布的详细说明。 架构设计和实现是开发人员主要关注的阶段。
大多数开发人员喜欢从事体系结构,以考虑如何从头开始安排系统或系统的一部分。 如果有人考虑了系统的体系结构并将其实现,那么动机就没有问题:程序员将从实现其思想中获得满足。 但是,如果一个人想到了体系结构,而另一个人将从事实现,那么后者可能会自然而然地发怒:他们为我想了一切,但我能做些写的吗?

在本文中将讨论如何避免这种情况,以及为什么实现与架构的阐述一样有趣,有时甚至更多。
引言
经过深思熟虑的体系结构可以用作分解任务的基础:每个充分独立的组件的实现都将成为独立的子任务。
例如,如果有一个用于处理请求的管道(在结构上以管道和过滤器的样式构想),则子任务将是各个处理步骤的实现(每个步骤都有其自己的子任务)和另一个将所有步骤连接在一起的子任务。
尽管经过深思熟虑的体系结构和细分为子任务可以大致了解如何创建系统并允许您评估人工成本,但它们不足以实施计划。 子任务的描述将说明组件应执行的操作,它可能包含对速度和内存消耗的要求,但不会提供有关如何执行操作的全面说明。
事实是,有很多选择可以制造满足指定要求的组件。 在很大程度上取决于它的实现方式:代码的灵活性,其可扩展性,易于支持等。 我们接近代码设计的概念。
代码设计概念
有时,代码设计称为架构或代码组织,有时甚至称为架构。 我坚持使用术语“ 代码设计”,因为它与系统体系结构进行了对比,并在它们之间划清了界限。 更具体地说,考虑一个例子。
假设我们正在开发越来越受欢迎的网站的后端。 服务器的数量已经超过了几十个,并且受众不断增长,我们决定要收集有关网站上用户行为的分析:访问页面的受欢迎程度,取决于用户个人资料的功能使用频率等。
出现了许多体系结构和技术问题:度量的存储位置,如何通过网络传输度量,如果度量存储不可用该怎么办,后端服务将如何记录度量等等。 架构只需要回答这些问题,确定解决方案的组件并为其设置要求。
假设我们已经开发出一种体系结构:我们将使用InfluxDB作为存储,使用UDP通过网络传输度量标准以进行Telegraf ,并且为了避免存储的不可用性,我们将度量标准存储在跨多个服务器复制的Kafka中。 所有指标都将遵循后端服务-> telegraf-> Kafka-> InfluxDB的方式。 为了编写度量标准,后端决定编写一个模块,该模块使用UDP在telegraf中实现度量标准传递功能。
记录指标的模块是系统的单独组件,其编写是可以委托开发人员的单独子任务。 该子任务有许多解决方案和需要解决的问题:度量将同步或异步发送; 如何同步同步访问多个后端线程,主要的类/函数是什么。
这些问题超出了解决方案体系结构的描述,但对它们的答案却产生了深远的影响。 例如,如果在解决方案运行期间很明显技术堆栈不是最佳的,并且您需要用替代解决方案替换telegraf,则将模块错误地划分为多个类将不允许这样做,而无需重写整个模块。 这些问题的答案是代码设计的领域 。
代码设计的开发是一个单独的设计阶段 ,介于系统体系结构的开发和编码之间。 在体系结构和代码设计之间划清界限,使您可以在不考虑所有细节的情况下设计系统,并在有限的时间内评估人工成本。 另一方面,将代码设计的开发突出显示为一个单独的实现阶段,可以使您提高系统实现的质量,减少进一步改进的成本,并增加支持的便利性。
在编码之前需要在实现阶段考虑代码设计的问题使实现变得有趣 :设计代码设计的任务与在体系结构级别设计整个系统一样有趣。 布鲁克斯在神话中的月刊中表达了这个想法。
当然,在体系结构和代码设计之间划清界线可能不是那么容易,让我们仔细研究一下这个问题。
架构与代码设计之间的界限
从意识形态上讲,体系结构和代码设计处于不同的设计级别:体系结构是在最初阶段就确定的,当时尚无确定性,对代码设计的思考会增加细节。 因此,它们在不同的时间点执行:体系结构更接近最开始,而子任务实现期间的代码设计。
在这两个设计阶段之间划定界限取决于许多因素,以下是主要因素:
- 组件影响系统的程度。 有时,整个系统的设备可能在很大程度上取决于其各个组件的设备。 在这种情况下,您需要在架构开发阶段而不是在实施阶段设计组件。
- 组件有清晰的接口。 仅当明确定义了该组件应该做什么以及如何与系统其余部分进行交互时,才可以将组件的设计隔离为子任务。
- 完成子任务的实际估算。 任务可能太大,无法以足够的准确性评估人工成本。 在这种情况下,最好更详细地设计任务并将其分解为自己的子任务,以便对劳动力成本进行更充分的评估。
在几种特殊情况下,您可以在体系结构设计和代码设计之间划清界限。
该组件具有严格的API。
例如,在我的实践中,有一个任务:在UNIX套接字API之上实现,以捕获/释放现有守护程序使用的OS资源。 这项任务是在新的史诗功能的选定架构的框架内产生的。 在该架构的框架内,描述该API相当高级,并且稍后在实现过程中进行了详细的设计。
具有给定接口的模块/类
委托整体系统的一部分设计的最简单方法是突出显示模块或类,描述其接口和任务。 分配为单独的子任务的模块不应太大。 例如,用于访问分片数据库的客户端库无疑是一个单独的模块,但是如果没有更详细的设计,实现此库的任务将很难通过人工成本来评估。 另一方面,实现一个很小的类的任务将是微不足道的。 例如,如果出现子任务“实现通过给定路径检查给定文件夹是否存在的功能”,那么显然会考虑过多的体系结构。
具有固定要求的小组件
如果组件足够小,并且严格定义了要解决的问题,则可以以足够的准确性估算实施所需的人工成本,并且组件本身的实施将为设计留出空间。 示例:在冠上运行并递归删除给定路径上的旧文件和目录的进程。
反模式
在某些情况下,对架构和实现的思考之间的分配不正确,下面将讨论其中的一些情况。
一切都按照最小的细节进行设计。
建立了详细的UML图,指定了每个类的每种方法的签名,描述了用于实现各个方法的算法...根据这样的详细描述,您可以以最快的速度实现该系统,实际上是因为所有事情都计划得如此详尽,以至于根本没有创造力的余地,请动手去做。书面的。 如果目标是开发人员尽快对他们说的话进行编码,那么可以。
但是,如果您进行更深入的研究,很显然,在组织工作方面存在许多缺陷。 首先,为了对所有细节进行设计,您将不得不在设计本身上花费大量时间。 开发人员通常在实施之前会想到的是,架构师会在此方案中进行仔细考虑:所有设计都移至项目开始时,这会延长项目持续时间。 毕竟,如果您不将设计工作分解成几个部分,那么您将无法并行化它。 其次,在实施过程中缺乏设计工作将极大地降低开发人员的动力:完全按照他们所说的做对初学者有用,但是有经验的开发人员会感到无聊。 第三,这种方法通常会降低输出质量:未分为足够独立的组件的系统将更难以维护和扩展。
架构始终由一名开发人员设计,其余人员 抽烟 只意识到
首先,应注意几种可能有用的情况。 首先,这是一个团队,其中有很多初学者,只有一个经验丰富的程序员。 在这种情况下,初学者没有足够的经验来设计体系结构来以足够的质量完成工作,而同时,深思熟虑的体系结构的实施将帮助他们提高水平。 其次,这些是涉及多个团队的大型项目。 然后,将项目架构的设计分为两个层次:架构师将其作为一个整体进行思考,以及每个团队-其职责范围内的组件架构。
但是考虑一支由经验丰富的专家组成的团队。 如果将架构任务始终只分配给一位,例如最有经验的开发人员,那么其他开发人员将无法充分展示其功能。 系统架构将是单方面的,因为每个人都有他所应用的一组技术。 如果不同的开发人员想到不同的组件/子系统的体系结构,这将有助于经验的交流和团队成员的发展。 即使经验不足的团队成员有时也应该承担架构任务:这将提高他们的水平,并增加他们对项目的参与。
结论
实现中设计阶段的存在是使实现任务变得有趣的主要因素。 当然,还有其他一些:新技术的使用,研究任务,但通常它们很少见。 如果实现任务不需要设计并且将由简单的编码组成,那么这将极大地影响开发人员的动力,并且将不允许他们使用其技能。
在实施阶段设计代码设计可以使您快速地对劳动力成本进行适当的估算,更有效地并行化工作,并总体上提高系统质量。
在实现过程中设计代码设计的需求正是使开发工作在开发人员眼中变得有趣的原因。
将设计工作排除在子任务的实现之外是不值得犯的错误,就像您不应该总是将架构任务仅委托给最有经验的开发人员一样。