超越 DRY 代码
作者:互联网
超越 DRY 代码
不,但真的, 为什么 代码重用是好事吗?
新软件开发人员学习的首要规则之一是无处不在的“不要重复自己”或 干燥 .在最基本的情况下,它通常被应用为“如果你发现自己在复制和粘贴一段代码,你应该把这段代码变成它自己的函数”。
该规则引用的通常好处是“代码重用”。多年来,当我学习编写软件时,我认为“代码重用”的好处是方便。 “我需要在我的代码中执行一项任务, 你去吧 ,我已经编写了一段代码可以做到这一点!”
不幸的是,您的编辑器的复制/粘贴功能已经提供了上述好处。键入单个函数的单行调用并不比复制和粘贴 20 行代码块快。
那么为什么代码重用好呢?我不否认它是,但推理更微妙的是短语“代码重用”所暗示的。当我遇到一些我一直认为理所当然的事情,或者我对它的理解很肤浅时,我喜欢使用“递归为什么”的方法来尝试找到根本原因或更深入的理解。一直问为什么,就像一个三岁的孩子,直到你得到一个满意的答案。
问:为什么将重复的代码块提取到可重用的函数中是好的?
A:所以我不必复制和粘贴代码
问:好的,这是我们肤浅的回答。为什么复制和粘贴代码不好?
A:因为那时我在我的应用程序中重复了代码块,它们都在做同样的事情
问:很好,但为什么重复的代码块不好?
A:因为如果我想改变一个,我必须记得把它们都改变
问:现在我们正在取得进展。为什么这么糟糕?
答:因为遇到此代码的任何人都需要了解其工作原理的所有底层细节。因为将需要更改此实现的所有地方都记在心里或记录在案的列表是乏味且容易出错的,尤其是因为将来我可能不会进行更改。
问:考虑到这一点,为什么提取重复的代码块是个好主意?
A: ** 因为它隐藏了实现细节 如何 这个特定的代码块有效,减轻了开发人员的精神负担,他们可以简单地使用它并相信它 做 工作**
DRY 只是表面上关于代码重用。 DRY 确实是大多数开发人员第一次遇到设计自己的抽象。创建可重用函数并不是为开发人员节省击键。这是关于隐藏开发人员不需要理解的细节,并暴露那些细节。
DRY 是关于抽象的
当你看到一段重复的代码并且你的 DRY 警报开始响起时,你不仅需要考虑如何将第 47 行到第 64 行提升到它们自己的函数中并调用它,更重要的是你需要批判性地思考这样做的目的是什么功能服务。您不仅要避免重复代码,还要将复杂的流程打包成一个或多个更小的命名工作单元。要做到这一点,请考虑以下事项:
- 这个函数的职责是什么?你能在不使用“和”这个词的情况下用一句话描述它的作用吗?
- 调用此函数的开发人员可以完全隐藏其实现的哪些细节?
- 为了完成它的工作,它需要接受什么作为论据?
- 它应该返回什么?
几周前的一个 发布了一段视频 展示了从小型应用程序的视图层中提取和抽象业务逻辑的过程。由于时间原因,我跳过了为时间数据的显示格式创建抽象,但今天我将干掉那段代码,着眼于 抽象 ,而不仅仅是代码重用。
初步方法
在里面 原始代码 ,有两个事件处理程序必须处理更新 UI 中的时间数据。首先是 开始
处理程序,它创建一个每秒递增一个计数器的间隔,然后将该计数写入 MM:SS
格式化到屏幕上。然后 重置
处理程序,它只是将该计数器重置为零并显式写入 00:00
到屏幕
乍一看,这可能不像是重复的代码,但从概念上讲,我们两次执行相同的任务进程:我们正在更新 时间流逝
变量作为我们应用程序内部状态的一部分,并且我们正在将该值的格式化表示以 a 的形式写入屏幕 MM:SS
展示。
事实上,我们可以将重置处理程序中的值写为 00:00
最初看起来很方便,但它之所以方便,只是因为我们忽略了游戏中的概念抽象。如果我们决定将显示格式更改为包含十分之一秒或小时,我们将不得不在这两个地方更新该格式。
换句话说,开发人员必须通读我们所有的代码并理解所有的实现决策才能自信地使用它。
因此,让我们首先尝试将其提取到可重用的东西中。一开始你可能很想做这样的事情:
这有点好,因为现在担心 如何 我们的时间被格式化在一个规范的位置上表示,但是我们有很多方法可以改进这一点。
那么它有什么问题呢?
让我们回到我在文章前面提到的四个问题:
问题一:这个函数的职责是什么?你能在不使用“和”这个词的情况下用一句话描述它的作用吗?
正如它目前所写的那样,我认为我们可以描述这个函数的最简洁但完整的方式是
转换秒数
_时间流逝_
成一个_MM:SS_
格式化字符串 和 将该字符串写入提供的 HTML 元素_时间显示_
.
那句话中的“和”很关键。这个函数有两个职责,一个是 代码气味 .我们在这里混合了两个问题:字符串的格式并将其写入 UI。巧合的是,在这两个地方我们格式化了我们的 时间流逝
值我们立即将其写入 UI。不能保证永远都是这样。当您想将格式化的时间字符串插入更长的字符串并显示它时会发生什么?不能用当前的实现来做到这一点。
这是一种紧密耦合的实现,使得它难以扩展和测试。您不可能预见到代码的所有使用方式,但您可以尽最大努力使其各个组件尽可能可组合。这就是您如何让您的未来生活更轻松,而无需预测未来。
关注点的耦合也使测试变得不必要地困难。按照目前的设计,如果您想为从数字创建格式正确的时间字符串的逻辑编写测试,您需要在某种渲染环境中运行该测试并断言 HTML 元素的内容。
问题 2:调用此函数的开发人员可以完全隐藏其实现的哪些细节?
正如目前所写的那样,我们并没有向我们的队友或未来的自己隐瞒太多的复杂性。我希望新开发人员必须通读此函数的实现才能知道发生了什么。函数名称中没有任何内容表明我们正在格式化输入,因此作为项目的新开发人员,我不清楚我应该传递原始秒数还是已经格式化的字符串。此外,时间显示应该或将采用什么格式?必须去阅读实现以找出答案。
问题 3:为了完成它的工作,它需要接受什么作为参数?
这里没有什么好争论的。它需要 时间显示
元素和 时间流逝
数字。这是函数完成其工作所需的两条信息,但是当我们根据前一个和下一个考虑这个问题时,我们可以做得更好。
问题4:它应该返回什么?
按照设计,我会说没有任何值得从这个函数返回的东西。我们可以返回格式化的字符串,但这使得返回值有点像是函数结果的事后想法。当我们用格式化的字符串更新 UI 时,调用代码希望该字符串用于其他用途的可能性有多大?不是特别的。如果确实如此,那么您只是通过使用从更大的操作中偶然返回的这个值来进一步混淆责任。
改进版
上述所有问题的答案都表明我们试图在函数中投入太多。这里要提取的真正抽象是 只要 字符串的格式:
通过仅将格式化逻辑提取到它自己的函数中,我们能够隔离并轻松组合各个关注点。这一变化为我们所有的问题提供了令人满意的答案:
这个函数的职责是什么?你能在不使用“和”这个词的情况下用一句话描述它的作用吗?
此函数转换秒数
_时间流逝_
成一个_MM:SS_
格式化的字符串。
简单的。说到点子上了。没有和。
调用此函数的开发人员可以完全隐藏其实现的哪些细节?
几乎所有人。该函数的新名称准确地说明了它的作用: formatSecondsToMMSS
.我们如何从输入到返回值的实现细节对于调用函数的开发人员并不重要。
为了完成它的工作,它需要接受什么作为论据?
只是几秒钟,函数名称暗示了这一点。
它应该返回什么?
格式化的字符串,再次由函数名称暗示。这个返回值是函数执行的工作的明显高潮。
这现在是一个纯函数(一个没有副作用,并且仅基于其参数返回确定性结果),这意味着它可以在代码中的任何地方轻松调用,您需要几秒钟并需要格式化的字符串。现在为它编写一个测试很简单。
从这往哪儿走
这个例子有点微不足道,就像所有简单的插图一样,掩盖了一些你必须在更现实的环境中解决的细节。
上下文和命名约定
一方面, formatSecondsToMMSS
仍然可能不是一个很好的函数名称。如果您正在编写一个真正的计时应用程序(例如,计费时间跟踪器),您可能至少有几种不同的方法来格式化时间,这取决于上下文。
因此,在这种情况下,假设您使用的是相同类型的命令式 vanilla JS 架构,您可能有一个名为 格式TimeForTrackerDisplay
,这可能会调用更灵活的 格式时间
功能。
这为您提供了一种灵活的低级方法来操作时间格式。但它也为 timerDisplay 定义了一个规范格式,可以从应用程序的多个位置调用它。
函数、方法、类、模块、组件……
我在此示例中使用了裸函数,因为它们简单且具有说明性。
但是在考虑为更高级别的构造设计的抽象时,所有这些问题都是相关的。你如何设计成可组合的,需要尽可能少的隐式上下文,并使它们的实现对消费开发者来说直观和明显?
更高级别的 UI 库
在上面的解决方案中,您可能注意到这行在两个处理程序中都重复了:
timeDisplay.innerHTML = formatSecondsToMMSS(timeElapsed)
在我们的小玩具应用程序中,无需进一步抽象此 UI 更新,因为它已经是一个简单的单行调用。在实际应用程序中,这暴露了可能会过时的实现细节。
如果我们要改变标记怎么办 时间显示
?我们可能必须通过设置其 innerHTML 来更新我们更新其值的所有行。
React 和 Vue.js 等更高级别的框架为您解决了这个问题,为您提供了一种声明式语法来编写一个用于时间显示的组件,它可能会收到 时间流逝
变量并在该值更改时自动更新显示。这消除了在应用程序周围的多个位置强制更新显示文本的需要。
但是,关于编写好的抽象的所有相同问题仍然适用,它们现在只适用于您的组件。
最后的想法
我一直在谈论消除隐式上下文和使函数名称具有描述性,您可能会认为我正在为“自我记录”代码提出论据。
离得很远。没有这样的事情。
软件开发既复杂又困难,在团队环境中总是需要一定程度的文档。
通过对抽象设计做出深思熟虑的决定,你 能够 使代码使用起来更直观,耦合更松散,更容易测试。所有这些都会带来更快乐、更健康的代码库,更重要的是,让队友更快乐、更健康。
最初发表于 https://benwilhelm.com .
可组合:像乐高一样更快地构建应用程序
[
少量 是一种以模块化和协作方式构建应用程序的开源工具。可组合以更快、更一致、更轻松地扩展。
→ 学到更多
将应用程序、页面、用户体验和 UI 构建为独立组件。使用它们更快地编写新的应用程序和体验。将任何框架和工具带入您的工作流程。共享、重用和协作以共同构建。
帮助您的团队:
→ 微前端
→ 设计系统
→ 代码共享和重用
→ 单仓库
学到更多
[
我们如何构建微前端
构建微前端以加速和扩展我们的 Web 开发过程。
博客.bitsrc.io
](/how-we-build-micro-front-ends-d3eeeac0acfc)
[
我们如何构建组件设计系统
使用组件构建设计系统,以标准化和扩展我们的 UI 开发过程。
博客.bitsrc.io
](/how-we-build-our-design-system-15713a1f1833)
[
如何在你的项目中重用 React 组件
最后,您完成了在应用程序中为时事通讯创建出色输入字段的任务。你很满意……
比特云
](https://bit.cloud/blog/how-to-reuse-react-components-across-your-projects-l3bhezsg)
[
构建 React Monorepo 的 5 种方法
构建生产级 React monorepo:从快速构建到代码共享和依赖。
博客.bitsrc.io
](/5-ways-to-build-a-react-monorepo-a294b6c5b0ac)
[
如何使用 Bit 创建可组合的 React 应用程序
在本指南中,您将学习如何使用 Bit 构建和部署成熟的可组合 React 应用程序。建立一个…
比特云
](https://bit.cloud/blog/how-to-create-a-composable-react-app-with-bit-l7ejpfhc)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明
本文链接:https://www.qanswer.top/23230/20580921
标签:DRY,格式化,函数,超越,代码,开发人员,应用程序,我们 来源: https://www.cnblogs.com/amboke/p/16674003.html