首页 » 99链接平台 » 更好的编码方式:文档驱动开发DDD(文档代码开发测试驱动)

更好的编码方式:文档驱动开发DDD(文档代码开发测试驱动)

雨夜梧桐 2024-11-23 07:19:20 0

扫一扫用手机浏览

文章目录 [+]

因此出现了测试驱动开发TDD的概念。

TDD 的核心理念是在实现代码之前,先编写单元测试或集成测试。

如果你先写代码再写测试,就会错过测试驱动开发带来的一个关键优势。

更好的编码方式:文档驱动开发DDD(文档代码开发测试驱动) 99链接平台
(图片来自网络侵删)

通过先写测试,TDD 迫使你在实现代码之前先设计好 API,这样可以确保 API 设计得更清晰、更合理。

而如果你先写代码再补测试,可能会导致 API 设计不够完善。

但是,测试仍然是一种编程形式,对产品经理要求太高。

现实是:产品不太懂研发,研发又不太懂业务。

TDD也很难落地。

如何设计一个大家都能懂的统一语言进行对话?

API 接口设计很难

举个例子,你在一家独立游戏公司工作,要实现一个名为 calculateUserScore 的函数,根据玩家在视频游戏中的 K/D 比例来计算得分。

输入一个基本的实现:

function calculateUserScore({kills, deaths}) { return parseInt(kills / deaths, 10)}

但是,助攻应该计算在内吗?当然应该。
我们可以将助攻视为半个击杀。

function calculateUserScore({kills, deaths, assists}) { const totalKills = kills + (assists / 2); return parseInt(totalKills / deaths, 10)}

某些击杀应获得额外积分。
例如,360度无瞄准击杀。

之前 kills 是一个数字,现在改为对象数组。

const killsArr = [ { additionalPoints: 3 }]

现在我们可以改变这个函数的实现:

function calculateUserScore({killsArr, deaths, assists}){const kills = killsArr.length;const additionalPoints = killsArr.reduce((prev, k)=> k.additionalPoints,0);const totalKills = kills +(assists /2);return parseInt((totalKills / deaths)+ additionalPoints,10);}

尽管我们已经修改了函数的实现,但请记住,游戏中的多个部分可能会使用这个计算。

此外,这个函数的 API 可能仍然不是最佳选择。

例如,如果你想在比赛结束后显示带有额外积分的特殊击杀,该如何处理?

这些大规模重构每次迭代都需要额外的工作,可能会延迟任务完成时间,从而影响发布日期或其他项目的启动时间。

让我们退后一步思考,为什么会发生这种情况?

这些问题通常是由于沟通不畅造成的。
这种沟通不畅可能发生在团队之间、个人之间,甚至在你自己的思维过程中。

测试也很难

许多人建议使用 TDD 来解决这个问题。
TDD 通过增加反馈循环,可以帮助你提前处理 API 的设计。

例如,在实现 calculateUserScore 函数之前,可以编写测试来验证第一个实现。
通过添加一个 test.todo 来提醒自己加入助攻的计算,并在继续之前更新 API。

然而,尽管 TDD 迫使你处理 API 的设计,它并不能帮助你理解范围的变化。
这种局限性可能反过来影响你的 API。

假设在开发周期的后期之前,你无法在比赛结束时显示特殊击杀的能力。

你知道这一点,因此在 kills 仍然是一个数字的第二次实现时停止。

然而,由于该函数在代码库中被频繁使用,你需要在以后进行大规模重构。

如果你之前与其他工程师沟通,可能会意识到比赛结束界面的开发比预期提前完成

不幸的是,这种情况只是在代码审查中被发现,迫使你立即进行重构。

有啥办法优化?

有一种比 TDD 更好的方法来解决“API 优化”问题,那就是“文档驱动开发”。

编写文档可以帮助我们在实施设计之前提前梳理实现细节,避免在实施设计时做出艰难的决策。
参考现有的 API 也可以帮助你进行大量设计。

回到之前的例子 calculateUserScore,假设你召集团队开会收集需求。

但这一次,你从撰写文档开始,而不是直接从编写代码开始。

在文档中,你应该包含根据这些需求描述 API 应该是什么样子的部分。

/ 此函数应根据玩家的 K/D(击杀/死亡比)计算用户得分。
助攻应计为半个击杀。
TODO: 添加特殊击杀并加分/function calculateUserScore(props: {kills: number, deaths: number, assists: number}): number;

您还可以在文档中加上一些用例数据:

caluculateUserScore({kills: 12, deaths: 9, assists: 3});

在撰写这些文档时,你决定快速草拟一下未来在添加额外积分时 API 可能会是什么样子。

/ TODO: 未来,为了适应额外积分,它可能会看起来像这样 /calculateUserScore({kills: [{killedUser: 'user1', bonusPoints: 1}], deaths: 0, assists: 0});

在写完这个之后,你意识到应该先使用数组来表示 kills 属性,而不是之后再修改。

你不需要立即添加额外积分,而是可以简单地为每次击杀跟踪一个unknown,以后再进行修改。

calculateUserScore({kills: [{killedUser: 'unknown'}], deaths: 0, assists: 0});

虽然现在这看起来可能很明显,但在当时可能并不那么清楚。
这就是文档驱动开发的好处:它强迫你在 API 和工作范围上进行自我反馈循环。

细化流程

文档通常被我们视为一项苦差事。
但并非如此。

文档可以采用多种形式存在,如设计模型、API 参考文档、详细的任务描述和未来计划的撰写等等。

基本上,任何能传达你对某个主题思考的东西都可以称作文档,包括测试。

测试是传达API使用示例的好方法。

有时,测试驱动开发(TDD)本身就足以为未来的自己提供这些信息,同时它也可以作为其他形式文档的有力补充。

特别是在编写开发工具或库时,展示如何执行某项操作的使用示例对于理解和验证行为非常有帮助。

"文档驱动开发"(DDD)不提倡的另一个观点是"写一次就完成"的概念。

这种想法是一种误解,可能会对项目的范围和预算(无论是时间还是其他方面)造成伤害。

正如我们在 calculateUserScore 示例中展示的那样,你可能需要在继续进行最终发布之前修改你的设计:这是可以接受的。

文档影响代码,代码也影响文档——这种反馈循环对于 测试驱动开发(TDD)同样适用。

DDD不仅对于开发生产代码有帮助。

在需求沟通会议中,一个有效的方法是先编写代码注释,然后再编写解决方案。

这种做法可以让你在文档编写阶段(编写注释时)发现潜在的错误,而这些错误通常在时间和精力上的成本较低。
相比之下,如果在实现过程中发现错误,则修复成本更高。

通过这种方式,你可以向产品经理展示你知道如何在团队中工作,并找到明确的目标。

这些能力将有助于你朝着无误差实现目标不断努力。

小结

其实,文档驱动开发与行为驱动开发(BDD)和验收测试驱动开发(ATDD)有相似之处,都强调通过文档来验证代码背后的用户行为和功能。

所以,这么来看的话,DDD 可以被看作是这些方法的一种形式。

1、文档是沟通工具:文档不仅仅是代码注释或 API 参考,它包括任何能够帮助团队成员理解和交流项目思路的内容。

2、文档驱动开发能够提高项目质量:使用 DDD 能够更好地定义项目目标,减少边缘情况,提高最终产品的质量。

参考

https://unicorn-utterances.com/posts/documentation-driven-development

标签:

相关文章