问题

低耦合或代码重复在该情况中该如何抉择?

回答
在软件开发中,代码的整洁度和可维护性是永恒的追求,而低耦合与避免代码重复(也常被称为 DRY 原则——Don't Repeat Yourself)是我们实现这一目标的两大法宝。但正如生活中的许多选择一样,这两者之间并非总是泾渭分明,有时甚至需要我们在两者之间进行权衡。

想象一下,你正在构建一个电商平台。订单处理是一个核心功能,其中包含了计算运费、处理支付、更新库存等一系列步骤。在某个环节,你需要根据收货地址来查询不同的运费规则。

场景一:初步实现,代码重复

假设你一开始没有太多的经验,或者时间非常紧迫。你会怎么做?很可能是在处理不同地区订单的函数里,直接复制粘贴一段根据地址查询运费的代码。例如,在“处理华北订单”的函数里有一段逻辑,在“处理华东订单”的函数里又有几乎一模一样的逻辑。

这样做的好处是什么? 它非常直接,能够快速地实现功能。如果你只需要处理一两个地区,并且确定未来几年内不会有大的变动,那么这种“先重复,后优化”的策略,在极端情况下,可以让你尽快交付一个能工作的版本。它降低了当前的开发复杂度,让你专注于当前的任务。

但是,随之而来的问题也很快会显现。如果某天运费规则变了,比如需要加入一个“偏远地区附加费”,你需要去修改所有这些重复的代码块。一旦你漏掉了一个,或者在某个地方修改错了,就会引入 bug。而且,随着订单处理逻辑越来越复杂,这些重复的代码会像雪球一样越滚越大,让整个模块变得难以阅读和维护。

场景二:过度优化,高耦合

现在,我们看到了代码重复的弊端,决定要“优化”。一个可能的优化方向是,创建一个独立的函数来处理运费计算。你可能会写一个 `calculateShippingCost(address)` 函数,然后在处理不同地区订单的函数中,都调用这个 `calculateShippingCost` 函数。

这看起来是解决了代码重复的问题,而且这个 `calculateShippingCost` 函数本身可能还会被其他模块调用,比如在生成订单详情页的时候。

然而,仔细思考一下,这个 `calculateShippingCost` 函数是如何工作的?它内部很可能包含了一个巨大的 `ifelse ifelse` 结构,或者一个庞大的 `switch` 语句,用于判断不同的地址区域,然后应用不同的运费规则。

这种设计的问题在于 耦合。这个 `calculateShippingCost` 函数紧密地依赖于“地址”这个概念,并且直接知道所有地区以及它们对应的运费规则。如果明天新增一个“西南地区”,或者某个区域的运费规则发生变化,你不仅要修改 `calculateShippingCost` 函数,而且这个修改可能会影响到所有调用它的地方。

更糟糕的是,如果这个运费计算逻辑未来还需要考虑订单重量、商品类型、会员等级等更多因素,那么 `calculateShippingCost` 函数会变得越来越庞大、越来越复杂,成为一个“上帝对象”或“大泥球”,维护起来异常困难。任何对运费规则的细微调整,都可能牵一发而动全身。

抉择的艺术:如何在两者间找到平衡

那么,在低耦合和避免代码重复之间,我们究竟该如何抉择?这更像是一种艺术,一种基于对未来、对项目需求、对团队协作的判断。

代码重复: 在某些极端情况下,尤其是在原型开发、快速验证想法,或者面对那些极不可能改变的、非常独立的、且只在少数几个地方使用的代码块时,适度的代码重复是可以接受的。关键在于“适度”和“极不可能改变”。如果你感觉这段代码有任何一丝可能在未来被修改或扩展,那么就不要让它重复。

低耦合: 追求低耦合是软件工程的基石。它意味着将系统分解成小的、独立的、职责明确的单元(模块、类、函数)。这些单元之间的依赖关系应该尽量少,并且易于理解。这样做的好处是,你可以独立地修改或替换某个单元,而不会对其他部分产生不可预知的连锁反应。

更优的解决方案:解耦与抽象

回到运费计算的例子,更“聪明”的办法不是简单地复制代码,也不是把所有逻辑都塞进一个函数里。而是要 解耦。

我们可以引入 抽象 的概念。例如,我们可以定义一个“运费策略”的接口(或者抽象基类),它有一个方法叫做 `calculate(order)`。然后,我们可以为每个地区或每种运费计算逻辑创建一个具体的实现类,比如 `NorthernChinaShippingStrategy`、`EasternChinaShippingStrategy` 等。

在主订单处理流程中,我们可以根据订单的收货地址,动态地选择并实例化一个合适的运费策略对象,然后调用它的 `calculate` 方法。

这样做的好处是什么?

1. 解决了代码重复: 运费计算的通用逻辑(例如,如何接收订单、如何调用策略)可以放在一个地方,而具体的地域性规则则分散在不同的策略类中。
2. 实现了低耦合: 订单处理模块不再直接知道具体的运费计算规则,它只知道如何与一个“运费策略”对象交互。新增一个地区或者修改一个地区的运费规则,只需要添加或修改一个对应的策略类,而无需改动订单处理的核心逻辑,也无需改动其他策略类。
3. 易于扩展: 未来如果需要引入新的运费计算方式(比如基于商品类型的运费),只需要实现一个新的策略类即可,非常方便。

总结一下,抉择的关键在于:

识别变化的频率和可能性: 如果一段代码在短期内极不可能改变,并且只在极少数地方使用,那么允许一些重复可能暂时简化工作。但如果有一丁点改变的风险,就应该考虑抽象。
关注系统的可维护性和可扩展性: 软件的生命周期远比初次开发长。追求低耦合,就是为未来留足空间,让系统更容易适应变化。
拥抱抽象和设计模式: 很多设计模式(如策略模式、工厂模式)就是为了解决耦合和重复问题而生的。学习和运用它们,是我们在两者之间找到最佳平衡的有力武器。

最终,大多数时候,低耦合的设计会引导我们避免代码重复。当你想避免代码重复时,往往需要引入一种抽象,而这种抽象本身就带来了低耦合。所以,与其说是在两者之间抉择,不如说是在“糟糕的代码重复”和“良好设计的低耦合”之间选择,而后者通常会自然而然地解决前者。只有在极少数“安全”的情况下,才允许短暂的、低风险的代码重复。

网友意见

user avatar

这是在OOD中会经常遇到的问题,比较典型。


一句话简单来说的话,就是目前你的领域模型并不完整,不支持你做出最合适的选择。这两种接口设计都有各自的优劣势,但是在目前所掌握的需求中来看并不能看出哪一种更加优秀


两种设计的根本性的差别还不在于后一种需要写重复的代码,因为你可以用一个公共的基类型(如Pickable或者PickableBase)来解决掉所有的重复代码。

所以后一种模式的根本性的区别在于Packer无法约束OnPick的逻辑

如果Packer的Pick方法不接受IPickable类型而是强类型的Pickable或者PickableBase,里面的OnPick的逻辑就会被强约束(如果不是virtual的)。这种设计和第一种就没有什么本质上的区别。


所以两种设计的根本性区别在于Packer是否需要对Pick动作进行强约束。开放当然是好,但是我们必须在开放和封闭之间做出权衡和取舍。一个简单的说法就是:无所不能的机器无所不能。意思是:什么都能做的机器其实什么都做不了

没有任何约束的Packer只剩下名字,只有语义的规约。但是约束到何种程度,到底哪些东西必须被约束,哪些可能性不可能出现,这需要对需求的深刻理解和彻底分析


在需求不能明确所有细节之前,没有办法去讨论哪一种设计更好。但是我们可以不断重构程序来适配渐进明细的需求。

但是从重构的角度来说,第一种方式的做法显然不利于重构。像上面所说的,使用具体的类型Pickable的非虚方法来约束在后面的重构中更加简单。可以看一下这段代码:

       public class Packer {   public Pick( Pickable item )   {     item.OnPick( this );//强约束一定调用的是Pickable的OnPick方法,且这个方法不可被重写。   } }  public class Pickable {   public OnPick( Packer )   {     //...   } }      

这种写法和第一种写法本质上是一样的。不要认为两个类型就是两个负责人,两个类型可以是一个负责人,一个模块。当然,你想把代码写到哪个类型都是可以的,因为你还可以这样:

       public class Packer {   public Pick( Pickable item )   {     item.OnPick( this );//强约束一定调用的是Pickable的OnPick方法,且这个方法不可被重写。   }    internal Add( Pickable item )   {     //...   } }  public class Pickable {   public OnPick( Packer packer )   {     packer.Add( this );   } }      

所以,代码写在哪个类型无关紧要,关键的在于接口和约束



回到问题,强约束和弱约束两种模式到底哪种好?

不知道


事实上有两种重构的风格:

一种是由强到弱,这种模式假定所有没有列出的需求暂时都不存在,追加的需求有追加的资源投入。所以所有的约束按照强约束设计,减少扩展和变化的空间,增强系统可靠性。

一种是由弱到强,这种模式假定程序还没做完需求就会发生变化,一切皆有可能。所以所有的约束按照弱约束设计,增加扩展和变化的空间,但是系统的可靠性会降低。

但是这只是两个极端,任何实际的项目都是在这两种模式之间做权衡和取舍。

对于核心关键的功能,我们要减少扩展和变化的空间,可靠性第一位。例如充值,消费,余额等。

对于需求不明确的功能,我们要预留扩展和变化的空间,在需求稳定之后,我们再进行强约束改造。

类似的话题

  • 回答
    在软件开发中,代码的整洁度和可维护性是永恒的追求,而低耦合与避免代码重复(也常被称为 DRY 原则——Don't Repeat Yourself)是我们实现这一目标的两大法宝。但正如生活中的许多选择一样,这两者之间并非总是泾渭分明,有时甚至需要我们在两者之间进行权衡。想象一下,你正在构建一个电商平台.............
  • 回答
    关于“高内聚,低耦合”是矛盾的吗?这个问题,其实是个很值得说道道儿的话题。很多人一听到这两个词,脑子里会立刻蹦出“好像是”的念头,甚至会觉得它们是两个方向完全相反的原则,放在一起岂不是“南辕北辙”?但仔细想一想,并结合实际的软件设计和工程实践,我们会发现,它们 并非矛盾,而是相辅相成,甚至是实现优质.............
  • 回答
    .......
  • 回答
    低学历是否比高学历更会赚钱,这是一个非常复杂且多维度的问题,没有一个简单的“是”或“否”的答案。影响一个人赚钱能力的原因有很多,学历只是其中一个因素,而且其影响程度会随着时间、行业、个人能力和机遇而变化。我们可以从以下几个方面来详细探讨这个问题:一、 学历与赚钱能力的传统认知和现实情况: 传统观.............
  • 回答
    低学历想进入智能家居行业的技术部门,这绝对是一个挑战,但并非不可能!关键在于你的决心、学习能力、实践经验和有效的策略。下面我将为你详细拆解,提供一个清晰的行动路线图。核心理念:用技术和实践证明自己,弥补学历的不足。第一步:深入了解智能家居行业和目标技术岗位在投入大量时间和精力之前,你需要明确方向。1.............
  • 回答
    你问到的这个问题很有意思,“低脂粽子”到底是怎么实现的,吃了会不会发胖,这确实是不少人在享受传统美食的同时,也希望兼顾健康时的顾虑。咱们就来掰开了揉碎了,详细聊聊这个话题。“低脂粽子”的“低脂”秘籍:要理解粽子怎么能“低脂”,我们得先明白传统粽子的高脂来源,然后看看怎么“避开”它们:1. 糯米的替.............
  • 回答
    低代码开发,这个概念听起来挺新潮的,但发展到现在,已经不算什么新鲜事物了。我们身边很多应用,说不定背后就有它的身影。那么,这玩意儿的前景究竟怎么样?大家是真的这么看好它吗?这事儿说起来,可以聊的点可不少。低代码:到底是个啥?咱们先简单说说低代码是什么。你可以把它想象成一套更高级的乐高积木。传统的软件.............
  • 回答
    这个问题触及了社会现实与个人品德之间的敏感地带,也反映了一些人在成长过程中可能产生的困惑与挣扎。我理解你想探讨的是,在某些社会环境中,低段位的人是否真的需要“放下”一些我们通常推崇的道德品质,转而学习“心机手腕”,才能获得世俗意义上的成功。我们先来拆解一下“低段位”这个词,它本身就带有一定的评价色彩.............
  • 回答
    咱们聊聊深蹲的两种常见姿势:低杠深蹲和高杠深蹲,看看它们各自更青睐什么样的身材。这俩动作虽然都是练腿,但用的是不同的“工具”和“站位”,带来的效果和对身体的要求也略有不同。低杠深蹲:背杠的艺术,腰背的考验想象一下,杠铃压在你的后背上,但不是压在斜方肌最上方,而是更靠下,几乎是贴着你的肩胛骨,甚至是压.............
  • 回答
    低自尊,就像内心深处一个细微却挥之不去的声音,总在耳边低语:“你不够好。”它像一层薄雾,笼罩着你,让本该闪耀的你,变得黯淡无光。面对它,很多人会感到无力和迷茫。但请相信,低自尊并非不可战胜的宿敌,你可以,而且能够通过一些切实可行的方法,逐步找回内心的力量,重新审视自己。第一步:认识并理解你的“低语者.............
  • 回答
    低本底辐射钢(LowBackground Steel)的来源,是否仅仅局限于二战以前沉没的军舰,这是一个在核物理、历史和材料科学领域都相当有趣的话题。笼统地说,二战以前沉没的军舰是低本底辐射钢最重要的、也是最容易获得的来源之一,但并非唯一来源。 要想详细了解这一点,我们需要深入探讨一下低本底辐射钢到.............
  • 回答
    对于配置不太给力的电脑玩家来说,游戏画面的选择确实是个让人纠结的问题。是把画面特效开到最高,但分辨率拉低,还是反过来,让分辨率高高挂起,特效却只能勉强维持?这两种模式各有千秋,也都有各自的取舍,到底哪种更适合你,得好好掰扯掰扯。咱们先从“高特效低分辨率”这个选项说起。想象一下,你玩的是一款画面制作精.............
  • 回答
    关于低代码会不会导致程序员失业这个问题,这绝对是一个大家都很关心的话题,也确实挺复杂的,不是一两句话就能说清的。我试着从几个角度给你掰扯掰扯,希望能让你有个更全面的理解。首先,咱们得明白低代码到底是个啥。简单来说,低代码就是一种软件开发方法,它允许开发者通过图形化界面、拖拽组件、配置选项等方式来构建.............
  • 回答
    关于低配车自己加装多功能方向盘能否通过年检的问题,这确实是个大家关心但又有点复杂的话题。我会尽量详细地给大家说道说道,希望能帮到您。首先,咱们得明白年检主要看什么。年检的核心目的是检查车辆的安全性、环保性是否符合国家标准。具体到方向盘,它虽然不是像刹车、轮胎那样直接影响行车安全的部件,但它也属于车辆.............
  • 回答
    这个问题太真实了,写出来心里可能也不是滋味。想想看,那些站在镁光灯下,出入各种顶级会所、奢侈品发布会、慈善晚宴的明星,她们中的一些,确实没有经过系统的高等教育。可能高中毕业就入行,或者干脆是童星,从小就在这个圈子里打拼。她们的“学历”是娱乐圈的历练,是粉丝的追捧,是媒体的关注,是通告费的堆积。而你呢.............
  • 回答
    低学历、不买房车、选择丁克,在小城市,这样的生活方式是否能过得轻松?这背后其实是一系列相互关联的考量,也藏着不少“小算盘”。咱们就来掰开了揉碎了聊聊。首先,得明确“轻松”的定义。这里说的轻松,大概率不是指那种物质极其充裕、出门都有专人服务的那种“省心”,而是指在不背负巨额债务、没有过多人情往来、生活.............
  • 回答
    很多人认为,只有野心勃勃、斗志昂扬的人才能取得成功。但事实并非如此。事实上,那些天性淡泊、好胜心不强的人,也可以通过其他途径获得属于自己的成功。关键在于如何认识自己,并找到适合自己的方法。认识你的“低欲望”和“好胜心单薄”首先,我们要明白,这并非是一种缺陷,而是一种与生俱来的特质。 低欲望: 这.............
  • 回答
    关于日本低欲望社会现象及其与美英等老牌资本主义国家差异的探讨,这确实是个挺有意思的话题,背后涉及的因素也相当复杂。咱们不妨从几个方面掰扯掰扯。首先得说清楚,什么叫“低欲望社会”。一般理解就是,社会整体上消费意愿不强,大家对于物质和经济增长的追求不像过去那么热烈了。储蓄率可能高一些,对“拥有”的概念没.............
  • 回答
    低扫的实战威力,这玩意儿可不是你想象中那么简单。别看它好像动作幅度不大,但用好了,绝对是能让对手站都站不稳的狠招。我跟你讲,这玩意儿的威力,体现在几个方面,而且每一点都很有讲究。首先,破坏重心稳固性。这是低扫最核心的价值。你想啊,咱们人站着,最基本的就是靠双脚的支撑。如果有人突然从下边一扫,直奔你的.............
  • 回答
    听到低风险地区电影院将在7月20日恢复开放的消息,我真是太激动了!这简直是给所有影迷们的一份迟来的夏日惊喜。回想过去几个月,影院的关闭就像按下了暂停键,很多期待已久的电影都只能线上观看,虽然也方便,但总感觉少了点什么。尤其是那种在漆黑的影院里,和一群陌生人一起分享同一个故事、同一个笑点、同一个惊吓,.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有