Posted in

【第 3341 期】DRY 是糟糕抽象的常见来源_AI阅读总结 — 包阅AI

包阅导读总结

1. 关键词:DRY、抽象、编程、代码、设计模式

2. 总结:本文主要探讨了 DRY 原则在编程中可能导致糟糕抽象,以导航菜单代码为例,展示了从简单重复到尝试 DRY 化的过程,指出过早抽象可能存在问题,强调应等待观察代码演变以创建更好的抽象,实现职责分离。

3. 主要内容:

– 前言介绍文章讨论 DRY 原则导致不良抽象及改进方法

– 以简单示例说明 DRY 原则在初学者编程中的应用

– 导航菜单代码最初重复且易错,Jane 建议用循环 DRY 化,团队代码变长但不易出错

– Alice 提出用工厂模式使代码更简洁,但阅读代码变难

– 市场团队实验需求暴露早期抽象问题,Joe 借此维护代码库,创建更好的抽象,分离关注点

– 总结得出观察代码演变后设计库的模式

思维导图:

文章地址:https://mp.weixin.qq.com/s/x-aw-MVZQSCAzmxHk406NQ

文章来源:mp.weixin.qq.com

作者:Swizec

发布时间:2024/8/10 0:03

语言:中文

总字数:2076字

预计阅读时间:9分钟

评分:89分

标签:DRY原则,编程设计,代码维护,抽象设计,代码质量


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

前言

主要讨论了 DRY(Don’t Repeat Yourself)原则在编程中如何导致不良的抽象,以及如何通过更合适的设计模式来改进代码。今日前端早读课文章由 @飘飘翻译分享。

正文从这开始~~

我见过或写过的最糟糕、最难维护的代码,往往都是为了追求 DRY(不要重复自己)。这是工程师们最早学习的设计原则之一,我们常常对此过于狂热。

阅读 “不重复代码”(DRY)与简单代码

为什么要 DRY

当你学习基础知识时,“DRY” 是一个值得培养的好习惯。你应该总是把代码写成这样:

【早阅】邻近法则:设计中的原则与应用

 console.log(1)
console.log(2)
console.log(3)
console.log(4)
// ...

变成使用循环的 DRY 代码:

 for (let i = 1; i < 5; i++) {
console.log(i)
}

是的,这个例子虽然看起来有些幼稚,但在初学者编程课程中很常见,你在工作中不会写这样的代码。但是在处理更多层次的间接引用时,你可能会写出类似的代码。

假设 Joe 用最简单的方式构建了一个导航菜单:

 const NavigationMenu = () => {
return (
<ul>
<li>
<a href="/about">
<img src="question-icon.png" />
About
</a>
</li>
<li>
<a href="/contact">
<img src="person-icon.png" />
Contact
</a>
</li>
<li>
<a href="/buy">
<img src="cash-icon.png" />
Buy
</a>
</li>
// ...
</ul>
)
}

每个新项都是前一个项的复制粘贴,只是对标签、网址和图标做了小修改。非常重复。

Jane 看了 pull 请求后说这段代码有问题

我们都知道你在更新这个菜单时会很着急,因为它感觉像是一个你在睡觉时都可以做的不需要动脑的任务。

这时候最容易犯错了。😉

DRY 是如何出错的

Jane 建议用循环将这段代码 DRY 化。当你有一个重复模式并有小变动时,这显然是个不错的选择。

 const NavigationMenu = () => {
const items = [
{
url: "/about",
icon: "question-icon.png",
label: "About",
},
{
url: "/contact",
icon: "person-icon.png",
label: "Contact",
},
{
url: "/buy",
icon: "cash-icon.png",
label: "Buy",
},
// ...
]

return (
<ul>
{items.map((item) => (
<li>
<a href={item.url}>
<img src={item.icon} />
{item.label}
</a>
</li>
))}
</ul>
)
}

团队从 10 行代码变成了 28 行。但它不那么重复也不容易出错了!一行代码定义了每个元素的标记,这意味着你只需要修改一次。

Jane 对那个配置对象不太满意,但团队可以接受。pull 请求通过 ✅

使用工厂模式可以使代码更简洁

在按下合并按钮之前,Alice 有了一个想法 —— 那个配置对象看起来很烦人。Jane 和 Joe 没有去除重复性,他们只是把它分散到了其他地方。

【第3332期】使用现代JavaScript操作DOM的高效模式

下一个添加链接的人会复制粘贴一个对象,然后更改字符串值。这不太好。

她决定用老式的工厂方法写一个函数,输出每个配置元素。以后还可以扩展为更智能的工厂,添加更多逻辑。

 function makeNavItem(url, icon, label) {
return { url, icon, label }
}

const NavigationMenu = () => {
const items = [
makeNavItem("/about", "question-icon.png", "About"),
makeNavItem("/contact", "person-icon.png", "Contact"),
makeNavItem("/buy", "cash-icon.png", "Buy"),
// ...
]

return (
<ul>
{items.map((item) => (
<li>
<a href={item.url}>
<img src={item.icon} />
{item.label}
</a>
</li>
))}
</ul>
)
}

得益于 JavaScript 方便的对象创建语法,团队的代码回到了 20 行。虽然是原来的 10 行的两倍,但非常 DRY。

  • 一个工厂返回每个配置对象

  • 你将它们收集在一个列表中

  • 遍历数据以渲染项

添加和删除项现在变得很简单。你甚至可以使它具有动态性,从内容管理系统中提取列表。

但除非 Jane、Joe 和 Alice 到处使用这种模式,否则阅读代码变得更难了。你必须跳来跳去,保持思维状态来理解它是如何工作的,而不是从上到下线性地阅读。

阅读 “不重复代码”(DRY)与简单代码

混乱的代码路径甚至不是最糟糕的部分。这是错误的抽象。

为什么这是一个糟糕的抽象 —— 职责分离

几个月过去了,市场团队想要进行一个实验:带红色边框的购买按钮会获得更多点击吗?

【第2677期】如何在React中应用SOLID原则?

Joe 看着代码,心都凉了。这种抽象设计是为了让所有按钮都保持一致。它们都被耦合在一起,并且没有为按钮提供向不同方向进化的可用性。

现在 Joe 面临一个选择:

  • 扔掉 DRY,重新写回简单代码

  • 添加更多参数来配置按钮

  • 重写抽象

其中有两个遵循 “你无法修复错误的抽象” 这一理念。其中一个使问题变得更加严重,走上了使用大量布尔参数来精心调整每次使用行为的道路。

这是工厂常见的问题。它们变得如此复杂,你不如直接写底层代码。

该团队的错误在于他们没有等待足够长的时间来观察这段代码的演变。当时看起来所有导航按钮都需要相同的外观。但有一个按钮与其他按钮不同,它具有不同的语义。

About 和 Contact 是导航项。Buy 是一个启动用户流程的动作。这是一个微妙但重要的区别,因为它表明购买按钮可能会在行为上发生变化。

这些微妙的区别几乎不可能提前注意到。但事后看来总是显而易见的。

创建更好的抽象

Joe 利用这个机会进行代码库的维护。之前的抽象是过早的,但市场团队要求的样式更改让他了解到如何分离关注点。

你有两个关注点:

这些可以变成 React 组件。

 const MenuItem = ({ href, style, icon, children }) => (
<li style={style}>
<a href={href}>
<img src={icon} />
{children}
</a>
</li>
)

const NavigationMenu = () => {
return (
<ul>
<MenuItem href="/about" icon="question-icon.png">About</MenuItem>
<MenuItem href="/contact" icon="person-icon.png">Contact</MenuItem>
<MenuItem href="/buy" icon="cash-icon.png" style={{ border: '1px solid red' }}>Buy</MenuItem>
</ul>
)
}

这种抽象使得例外处理变得容易。你无需在代码中对某个迭代的执行方式进行修改。

而使用 children 的组合模式可以轻松地渲染丰富的标签。如果你愿意,你可以在 Buy 按钮中添加任何附加标记。

流行的设计库进一步倾向于这种模式,通过提供 React 组件来处理 icon 属性。这样一来,您就可以更好地控制图标的渲染。

关注点的分离变成:

  • NavigationMenu 负责菜单的结构

  • MenuItem 负责每个项的结构

  • 每个渲染的条目负责项的值

这是在观察了数千个代码库数年的发展演变后得出的设计库模式。

关于本文
译者:@飘飘
作者:@Swizec
原文:https://swizec.com/blog/dry-the-common-source-of-bad-abstractions/

这期前端早读课
对你有帮助,帮”
“一下,
期待下一期,帮”
在看” 一下 。