我们应该始终使用TDD进行设计吗?

设计可以从TDD中浮现出来吗? 我们是否应该始终使用TDD设计软件? 我们应该预先设计吗? 我们应该在何时进行多少设计?

这些是常见问题,经常引起很多争论。 参与这些辩论的人们通常对设计及其定义的范围有截然不同的定义。 如果不先同意某些定义,就很难进行明智的讨论。

软件设计定义

让我们检查一下维基百科的内容:

软件设计是一个过程,代理通过该过程使用一组原始组件并受约束,创建旨在实现目标的软件工件规范。 软件设计可以指“在概念化,框架化,实施,调试和最终修改复杂系统中涉及的所有活动”,也可以指“在……(在……)程式化软件工程过程中,在需求规范之后和编程之前的活动。” –维基百科。

我发现此定义有点麻烦,并且使我想到了瀑布过程,在实现任何代码之前都要进行较大的设计阶段。

快速的Google可以向我们展示许多其他定义,其中大多数都说类似的话,但方式不同。 为了简洁起见,我不会在此处添加所有内容。 我将使用自己的定义:

软件设计是连续指定软件模块的连续过程,这些模块负责以易于理解,组合,更改和替换的方式提供良好定义的内聚行为。

此定义的问题在于,它不仅定义了软件设计,而且还引用了一种工作方式,这意味着它意味着设计应作为开发过程的一部分进行逐步完成,而不是完全预先进行。 对于那些不喜欢这种工作方式的人,我们现在可以将其作为描述的可选部分。

软件设计是[连续的]过程,用于指定负责提供定义明确且具有凝聚力的行为的软件模块,使它们易于(随时间推移)被理解,组合,更改和替换。

软件设计水平和影响

软件设计发生在许多不同的级别,并且并非所有设计技术都适用于每个级别。 尽管系统可以显示多个级别的细节,但是为了简化起见,我将简化可以进行设计的不同级别。

体系结构:此级别的设计是定义不同系统的职责以及它们之间如何交互的过程。 在这里,我们还可以定义技术堆栈,持久性机制,安全性,性能,日志记录,监视和部署的通用策略。 错误的决策或此级别的更改会严重影响整个项目。

宏设计:此级别的设计是定义单个系统的总体结构的过程。 此结构可能与以下各层有关:传递机制与核心域的分离,六边形体系结构,组件,间接层,高级抽象,包/命名空间。 我们可能还会定义技术堆栈和上述某些架构问题,但仅适用于单个系统。 此级别的决策应影响系统本身,而不影响生态系统的其余部分。

微观设计:此级别的设计是在代码级别定义细节的过程,重点是在宏级别定义的高级概念的内部。 微观设计意味着定义类,方法,函数,参数,返回类型和一些小组件。 此级别的决策往往对系统级别几乎没有影响,而对体系结构级别则没有影响。

设计方法

除了进行设计的不同层次之外,还有其他不同的设计决策方式。 它们主要与我们做出这些决定的时间有关。

预先大型设计(BDUF):通常与瀑布式流程相关联,BDUF是预先设计整个系统(在开发之前)的过程,其范围可以从高级概念(体系结构,服务,协议,数据库)到低级概念。级别的概念(类,方法)。

战略设计:定义生态系统主要部分及其整体架构的过程。 与BDUF不同,战略设计未涉及细节。 其目的是根据最重要的业务需求提供技术远景。 这种高级愿景描述了服务,其职责,它们的通信方式,体系结构组件,主要业务流,持久性,API等。

及时设计:在即将构建软件模块时对其进行定义的过程。 此设计过程可用于宏观或微观设计。 实时设计在Outside-In TDD(伦敦学校)中很常见,在TDD的红色阶段,模块之间的协作被定义。 当使用Outside-In TDD时,重构阶段通常涉及较小的设计改进。

紧急设计:根据刚编写的代码定义软件模块的过程。 此设计过程通常在微观设计级别应用,并逐渐推向宏观设计。 紧急设计通常在古典主义TDD(芝加哥学校)中找到,该设计在TDD的重构阶段进行,以改进在Green阶段编写的现有工作代码。

复杂程度

复杂性是决定设计策略时需要考虑的另一种观点。 复杂性可以采取多种形式 ,并且超出了本文的范围,无法描述我们可以在软件项目中找到的所有类型的复杂性。 但是为了简洁起见,让我们考虑复杂的事情,因为我们无法立即理解或可视化解决方案。 复杂性可以存在于任何级别,包括体系结构,宏或微观设计。 根据复杂程度和发生的程度,应使用不同的设计方法。

TDD通常可以在微观级别上逐步使用,以逐步解决复杂问题,主要是在已知输入,输出和/或副作用的情况下。 例如罗马数字kata或计算保险费之类的算法。 但是,某些微观问题肯定会受益于一些先期思考,例如实施遗传算法。

设计发现过程

设计发现过程不仅应在不同级别上有所不同,还应根据我们对问题域的熟悉程度而有所不同。 无论我们是在架构还是微观设计层面,我们都应该能够立即设计出一个熟悉的问题。 在这种情况下,预先设计没有太多优势。 我宁愿在编写代码的同时进行增量设计。 但并非总是我们都熟悉问题领域,为了使风险降到最低,我们应该进行一些前期调查,创建原型,与业务人员或其他团队一起举办研讨会,并在白板上画一些图。

最后责任时刻与延误成本

明天我们将永远比今天知道更多。 在我们对项目知之甚少的情况下,过早做出重要决策可能会在很长一段时间内严重破坏项目。 我记得当投资银行的架构小组很早就决定我们的团队必须在一个新项目中使用数据网格时。 这个决定对项目非常有害,对我们的开发人员来说是痛苦的,正如我们在开发的最初几个月中发现的那样,我们可以以更简单,更快的方式构建相同的系统。 但是到那时,改变为时已晚。

一个非常明智的建议是在最后一个负责任的时刻做出建筑决策。 有一个著名的故事,一个团队推迟了使用哪个数据库的决策,并使用内存中的存储库构建了系统的核心。 当他们对这些功能感到满意时,他们意识到可以将所有内容存储在文件中,而不必使用数据库,这使得他们的系统更易于部署和使用。 这很棒,但这只是故事的一方面。 另一方面,由于系统无法保留数据,因此他们花了一年时间上线。 推迟对他们的持久机制的决定也推迟了他们的上线日期。

延迟设计决策通常意味着延迟成本的增加。 与其延迟架构决策,不如将重点放在降低更改决策的成本上,以最小化架构组件之间的耦合的方式设计架构。

启用并行工作

当作为一个团队的一部分工作时,我们可以轻松地同意何时做出设计决策,因为不会影响其他任何人。 但是,在多个团队合作的环境中,需要预先进行一些设计,以使团队能够并行工作而不会造成很多干扰。 当他们在定义明确的接口或API后面工作时,将多个团队的工作集成起来也更加容易。

各个级别的并行和增量设计

持续部署是许多公司的目标,因此,我们需要开发垂直切片的功能,并不断改进设计的所有部分,从体系结构到微设计。 各个级别之间的差异通常是变化率。 在体系结构级别,我们将定义支持最重要功能(例如,将成为最低可行产品(MVP)或里程碑的一部分)的最低要求 。 这样,架构将每隔几个月发展一次,宏设计每隔一个月左右发展一次,而微观设计则逐日发展。

设计和测试步骤的大小

有时,宏观与微观设计之间的界线可能会有些模糊。 通常,我需要在使用即时设计和紧急设计之间做出选择。 决定与我的信心水平有关。 我对要提供给问题的解决方案越有信心,我的TDD步骤将越大。 在这种情况下,Outside-In TDD是我偏爱的TDD风格, 编写测试着重于行为和协作。 但是,有时我无法轻易地可视化解决方案,或者我不确定我脑子里的解决方案是一个好的解决方案。 在这种情况下,我转向古典主义TDD,并在测试中使用非常小的步骤,对红色阶段不做任何设计假设,并使用我能找到的最简单的实现尽快将其变为绿色,然后使用重构阶段来确定是否以及如何改善设计。 简单设计SOLID原则的 四个规则是我在重构阶段使用的设计准则。

试驾架构

有人说他们测试驱动架构。 可能是正确的,但我相信他们实际上使用了“测试优先”方法,并不一定能像从TDD中那样受益于快速的“红色-绿色-重构”短迭代序列来发展体系结构。 在“测试优先”方法中,进行测试是为了确保满足特定的功能或非功能需求,而不是作为逐步发现体系的帮助。 TDDing体系结构可能会浪费大量时间。 我更喜欢使用TDD的双循环,首先从验收测试为我提供指导,然后再使用单元测试来发展解决方案。 我也喜欢架构适应性功能的想法–我们在瑞银(UBS)让他们确保吞吐量和延迟处于可接受的水平–但它们并非完全是TDD。

之后可以编写测试吗?

在某些情况下,我会在进行体系结构和黑盒测试时这样做,因为我宁愿等待界面和业务功能稳定下来。 这些测试通常很难编写和维护(通常它们具有复杂的设置),因此在我对需要测试的内容以及如何使其工作有了很好的了解之后,我更喜欢编写它们。

我们通常遵循的准则

由于影响和复杂性,我们通常在编写任何测试或代码之前预先设计架构。 每个项目的前期如何变化。 我们倾向于增量架构,通常将下一个里程碑的功能用作我们的架构决策的基础。 我们接受风险可能会在接下来的里程碑中发生变化,但是事实证明,降低这种风险是非常浪费的。

在宏设计级别,我们通常会进行一些快速的白板会议,并讨论应用程序的主要组件。 我们也同意我们是否要使用层,六边形体系结构以及如何将传递机制和基础结构与核心域分离。 由于我们熟悉宏设计决策,并且倾向于在较小的垂直切片中开发代码,因此,Outside-In TDD是我们最喜欢的方法。

在微观层面上,我们的绝大多数设计决策都来自TDD,结合了由外而内和TDD的结合。 尽管来自外部,但我们经常在流程的一部分中使用小的步骤和三角剖分来冲刷算法,组件的详细信息,甚至一些基础结构代码。

结论

对我来说,问题不是我们是否应该预先设计,而是何时。 设计发生在许多不同的级别(体系结构,宏和微观),并且具有不同程度的复杂性。 我们还需要了解设计的消费者是谁。

从BDUF到Classicist TDD,都有不同的设计风格和技术。 我不是极端爱好者,所以我更喜欢介于两者之间,有时使用Emergent Design,但很少使用BDUF。

务实。 不要盲目遵循惯例。 有时在白板上画几个小时可以节省几个月的工作量。

翻译自: https://www.javacodegeeks.com/2018/05/should-we-always-use-tdd-design.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值