二即是多 » 代码简洁性
管理同意
为了提供最佳体验,我们使用Cookie等技术存储和/或访问设备信息。同意这些技术将允许我们处理数据,例如在本网站上的浏览行为或唯一ID。不同意或撤回同意可能会对某些特性和功能产生不利影响。
功能性
始终活跃
技术存储或访问对于明确请求的订阅者或用户使用特定服务的合法目的,或仅为了通过电子通信网络进行通信传输的目的是绝对必要的。
偏好
技术存储或访问对于存储订阅者或用户未请求的偏好的合法目的是必要的。
统计
技术存储或访问仅用于统计目的。仅用于匿名统计目的的技术存储或访问。没有传票、互联网服务提供商的自愿合规或来自第三方的额外记录,仅为此目的存储或检索的信息通常无法用于识别您的身份。
营销
需要技术存储或访问来创建用户配置文件以发送广告,或在一个网站或多个网站上跟踪用户以实现类似的营销目的。
管理选项 管理服务 管理{vendor_count}供应商 阅读更多关于这些目的 接受 拒绝 查看偏好 保存偏好 查看偏好 Cookie政策 {title} 印记
代码简洁性
二即是多
2015年12月21日 by Max Kanat-Alexander
我在进行增量开发和设计时遵循一个关键规则,我称之为“二即是多”。这是我实现《软件设计的三个缺陷》中“只需达到你需要的通用程度”规则的方式。
本质上,当我注意到自己 tempted 要剪切粘贴一些代码时,我就知道我的代码需要多通用,然后不是剪切粘贴它,而是设计一个仅满足这两个特定需求的通用解决方案。一旦我 tempted 想要某个东西的两个实现,我就会这样做。
例如,假设我正在设计一个音频解码器,起初我只支持WAV文件。然后我想在代码中添加一个MP3解析器。WAV和MP3解析代码肯定有共同部分,我不会复制粘贴任何部分,而是立即创建一个超类或实用程序库,只做这两个实现所需的事情。
这的关键方面是我立即这样做——我不允许存在两个竞争的实现;我立即制作一个通用解决方案。下一个重要方面是我不让它过于通用——该解决方案仅支持WAV和MP3,并不以任何方式期望其他格式。
这个规则的另一个部分是,理想情况下,开发人员永远不必以类似或相同的方式修改代码的一个部分,就像他们刚刚修改了另一个部分一样。他们不应该在更新B类时“记住”更新A类。他们不应该知道如果常量X改变,你必须更新文件Y。换句话说,不仅是两个实现不好,两个位置也不好。并不总是可能以这种方式实现系统,但这是要努力的目标。
如果你发现自己处于必须为某物设置两个位置的情况,请确保当它们不同步时系统会大声且明显地失败。编译应该失败,一个总是运行的测试应该失败,等等。应该不可能让它们不同步。
当然,这个规则最简单的部分是经典的“不要重复自己”原则——不要有两个代表完全相同事物的常量,不要有两个做完全相同事情的函数,等等。
可能还有其他适用此规则的方式。总体思路是,当你想要一个概念的两种实现时,你应该以某种方式将其变成单一实现。
在重构时,此规则有助于找到可以改进的地方,并提供一些关于如何进行的指导。当你看到系统中的重复逻辑时,你应该尝试将这两个位置合并为一个。然后如果有另一个位置,将其合并到新的通用系统中,并以此方式进行。也就是说,如果有许多不同的实现需要合并为一个,你可以通过一次合并两个实现来进行增量重构,只要合并它们确实使系统更简单(更容易理解和维护)。有时你必须找出合并它们的最佳顺序以使其最高效,但如果你无法弄清楚,不用担心——只需一次合并两个,通常你会得到一个解决所有问题的单一良好解决方案。
同样重要的是,不应在不应该合并的时候合并东西。有时将两个实现合并为一个会导致整个系统更复杂或违反单一职责原则。例如,如果你的系统中Car和Person的表示有一些稍微相似的代码,不要通过将它们合并到一个CarPerson类中来解决这个“问题”。这不太可能降低复杂性,因为CarPerson实际上是两个不同的东西,应该由两个单独的类表示。
这不是宇宙中硬性 fast 的法则——更多是我在增量开发时用于判断设计的强烈指南。然而,它在重构遗留系统、开发新系统以及通常提高代码简洁性方面非常有用。
-Max
分享 点击在Facebook上分享(在新窗口中打开) Facebook 点击在LinkedIn上分享(在新窗口中打开) LinkedIn 点击在Hacker News上分享(在新窗口中打开) Hacker News 点击在Reddit上分享(在新窗口中打开) Reddit 点击在Threads上分享(在新窗口中打开) Threads 点击在X上分享(在新窗口中打开) X
22条评论 发表回复
Alex Vincent 说: 2015年12月21日下午1:51
就个人而言,当有三个或更多不同的实现时,我会进行合并。当然,一个是最理想的。两个让我感觉不舒服,但有时有充分的理由。三个简直不可接受,是马虎代码的标志。
回复
David Cuccia 说: 2015年12月21日下午3:45
我同意Alex。在我早期,即使是一个实现,我也会过度设计某些东西。现在,我通常等待代码需要出现在三个不同的地方后才集中化它。很多时候,维护中央库的负担超过了避免同步带来的温暖模糊感。
回复
David Cuccia 说: 2015年12月21日下午3:45
(避免同步问题)
回复
Codesimplicity – Two is too many – yaleman.org 说: 2015年12月21日晚上9:51
[…] Codesimplicity – Two is too many […]
回复
Jens Bannmann 说: 2015年12月22日凌晨3:42
嗯,我支持Max:两个确实太多了。
虽然理论上,Alex等待第三个实现的方法感觉还可以,但我的经验表明,多年来,其他开发人员通常会盲目地遵循两个不同实现所建立的先例。等到有人最终想到合并它们时,可能不是三个,而是例如七个竞争的实现。在我看来,几乎总是从一开始就做更好。只有一个统一的实现也会增加可读性,并帮助新开发人员理解系统。
回复
Max Kanat-Alexander 说: 2016年9月18日晚上10:35
是的,根据我的经验,“二即是多”方法的优点在于它可靠地工作。可能有很多其他好的理论原因使用另一种方法,但我根本没有看到它们实际上能保持代码库的可维护性。
-Max
回复
Ian Thomas 说: 2015年12月23日凌晨6:21
“三即是多”方法的一个问题是,当你开始编写第三个实现时,你现有的实现可能已经分岐,它们需要工作才能合并到一个通用实现中。
回复
Max Kanat-Alexander 说: 2016年9月18日晚上10:34
是的,有很多问题。另一个是,注意到有两个实现比注意到有三个要容易得多。也就是说,可能有三个实现,但开发人员只找到另一个实现,所以认为只有两个,并等待有三个。而且是的,正如你指出的,合并三个实现可能比合并两个然后在你需要它做稍微不同的事情时增强合并的解决方案要 work 得多。在非常大的代码库上,这变得非常明显,在某些情况下合并 even 两个现有实现都很困难。
回复
Alex Vincent 说: 2015年12月23日上午7:08
就其价值而言,我也不是说这是一个硬性 fast 的规则。我发现,很大一部分与你有多少 capacity 和影响力来进行此类更改有关……但这更多是关于项目/时间管理,而不是一个 rank and file 高级工程师想做正确的事情。
我说两个实现让我感觉不舒服。当 practical 时,我会尝试合并它们,而不是等待第三个。
回复
Mark 说: 2015年12月23日上午8:40
谢谢提醒,Max。好东西。
回复
Jim 说: 2015年12月31日下午4:40
我同意,在Max的例子中,一个CarPerson类将是一个糟糕的(实际上,IMO,可怕的)想法;然而,有时将完全相同或几乎完全相同的代码或逻辑提取到一个单独的类或模块中,并通过你所使用语言中可用的任何适当机制(例如,某种类型的继承,或在Ruby中,“include”机制)在任何适当的类(Max例子中的Car和Person)中使用这个共享代码/逻辑是有用的。这样的设计在Car与Person的例子中可能是适当的(尽管,没有具体细节,我不能肯定地说)。我相信,在Meyer的继承分类中(http://se.ethz.ch/~meyer/publications/computer/taxonomy.pdf),这种情况通常会被归类为“实现继承”。
回复
Anil 说: 2016年1月10日凌晨1:29
再同意不过了。我们需要特别注意这一点,尤其是在维护别人写的旧代码时。在那里,我反复遇到,不仅仅是2个,而是至少5-6个地方你需要做相同的更改。我从事软件开发已经10年了(从C开始,然后转到C++,现在是Java),我正在将我的方法从OOP转向函数式,并看到生产力的显著提高。
回复
abdullah 说: 2016年2月19日上午8:35
作者有很多 valid points,肯定每个开发人员都应该努力实现提到的原则。但回到现实世界,预算和时间可能是个问题,你如何证明更改一个已经 functional 的系统部分是合理的?你的老板会高兴你现在必须回去测试一些不属于项目范围的东西吗?
另一个方面是,花时间制作一个通用实现可能很棒——但如果你从不重用那段代码呢?因此你的解决方案变得过度工程化,这意味着更长的开发周期(以及更多修复bug的努力)。
作为一个通用原则,这种方法是有道理的,但像任何事情一样,它需要 moderation 和常识来调和。
回复
Shalaka Virkar 说: 2016年5月11日下午12:16
这是一个非常 valid 的担忧。但我认为这是单元测试发挥非常重要作用的地方。如果你有好的单元测试作为安全网,那么那应该帮助你发现是否破坏了任何东西。如果需要,你应该添加更多。话虽如此,端到端测试没有替代品。一个好的测试自动化框架应该处理 that。如果时间允许,你也可以进行手动测试。
回复
Max Kanat-Alexander 说: 2016年9月18日晚上10:25
我在《测试哲学》中详细讨论了这个。
回复
Max Kanat-Alexander 说: 2016年9月18日晚上10:29
Abdullah,总的来说,我建议你阅读这本书,它回答了所有这些问题以及更多。
-Max
回复
Kamran 说: 2016年2月20日下午3:37
我编程多年,从高中起就弹钢琴和学习音乐(我47岁)。我看到编程和写作严肃音乐之间有很多相似之处。音乐中有一句话,类似于:“如果它能让你的音乐听起来更好,你可以忽略音乐中的任何规则”。编程或音乐(以及许多其他领域)中的规则或模型只是一些指南,一旦你掌握了它们,你就会看到它们的优点和缺点,然后可以在任何时候不使用它们,即使别人说,“嘿,你做的事情不对!”,如果你知道自己在做什么,让他们说什么都行。
回复
Shalaka Virkar 说: 2016年5月11日下午12:35
这是一个非常 valid 的担忧。但我认为这是单元测试发挥非常重要作用的地方。如果你有好的单元测试作为安全网,那么那应该帮助你发现是否破坏了任何东西。如果需要,你应该添加更多。话虽如此,端到端测试没有替代品。一个好的测试自动化框架应该处理 that。如果时间允许,你也可以进行手动测试。
回复
Dave Beck 说: 2016年7月3日下午1:21
复制粘贴就像往嘈杂的变速箱里倒锯末——看起来你取得了进展,但你以后会为此付出代价。
回复
Code Simplicity » Kindness and Code 说: 2017年8月12日上午8:07
[…] 这行代码很难理解,这看起来像代码重复。你能重构它,以便它 […]
回复
Kindness and Code - Best Application Development Platforms, Software, App Developers 说: 2017年8月16日凌晨4:18
[…] 这行代码很难理解,这看起来像代码重复。你能重构它,以便它 […]
回复
Ruslan Spivak: Let’s Build A Simple Interpreter. Part 15. – Cebu Scripts 说: 2019年6月22日凌晨4:10
[…] 我们可以移除上面的手动步骤(b),并仅在一个地方保留令牌类型。这是“二即是多”规则在行动——向前发展,添加新关键字唯一需要做的更改 […]
回复 发表回复 取消回复
联系 关于 书:理解软件 书:代码简洁性 输入您的邮箱… 订阅 Max Kanat-Alexander 6月27日
我目前认为,AI代理能够成功生成的输出数量和质量取决于:
- 模型的质量。
- 代理的质量。
- 输入的质量(如提示或其他上下文)。
- 代理可以独立运行的确定性、客观验证的质量。
我目前认为,除非你是模型开发者,否则最重要的部分是“确定性、客观验证”。
简单来说,如果你告诉AI做某事,它需要某种方式能够验证它做了正确的事,靠自己。在软件中,这通常意味着运行一个测试、一个linter等,如果系统做错了就会失败。
这意味着你的测试和验证工具越好,你从模型得到的输出就越好。这不仅仅是测试数量的问题。它们必须是好的测试,具有智能断言和良好的错误消息。
这也意味着代理成功涉及思考如何将任务分解成可以分别通过自动化测试、linter等客观验证的单个部分。
作为说明,输入的质量也很重要,并且在我们的控制之下。如果我们编写更好的文档和更清晰的代码,代理会做得更好。令人惊讶的是,几乎一切帮助人类编写代码的东西也帮助代理。
阅读更多2310分享 Max Kanat-Alexander 6月4日
技术债务有价值的想法 mostly 是一个 myth。当你做出一个糟糕的软件工程决策时,它会在几小时、几天或几周内减慢你的速度。那是上限。许多人认为需要几个月或几年才能看到在软件设计上偷工减料的影响,但那 simply 不是真的。它几乎立即开始。做正确事的成本通常是几个小时,也许一天,你几乎总是立即 recover 那个时间。也就是说,做对通常花费与做错相同的时间。“等等,什么?那怎么可能?我们偷工减料是为了节省时间!” 它几乎总是结果根本不节省你的时间!它使处理变更更加混乱。它使代码审查更长。它在测试期间以更混乱的方式失败,需要更长时间调试。它几乎从不节省你的实际时间。
偶尔,你的路径中有一些“岩石”如此巨大,你 simply 无法移动它。没有人应该花三个月设计一个新工具,只是为了你可以一次交付一个一天的功能。但那是技术债务决策中 tiny, tiny 的一小部分。
这变得更加疯狂,因为如果你在过程中做对一切(你总是重构系统,使其看起来像是设计来做 exactly 它现在做的事情),那么一切始终保持相对容易。但如果你偷工减料,一切变得 compoundingly 更困难,到
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码