为需要重复两次的函数编写函数始终是最佳实践吗?


131

我自己,当我需要做两次以上的事情时,我迫不及待地想编写一个函数。但是,当涉及到仅出现两次的事情时,就会有些棘手。

对于需要两行以上的代码,我将编写一个函数。但是当面对这样的事情时:

print "Hi, Tom"
print "Hi, Mary"

我很犹豫地写:

def greeting(name):
    print "Hi, " + name

greeting('Tom')
greeting('Mary')

第二个似乎太多了,不是吗?


但是,如果我们有:

for name in vip_list:
    print name
for name in guest_list:
    print name

这是替代方法:

def print_name(name_list):
    for name in name_list:
        print name

print_name(vip_list)
print_name(guest_list)

事情变得棘手,不是吗?现在很难决定。

您对此有何看法?


145
我把alwaysnever红色标志。有一个叫做“上下文”的东西,其中alwaysnever规则,即使总的来说很好,也可能不合适。当心那些绝对的软件开发人员。;)
异步

7
在上一个示例中,您可以执行以下操作:from itertools import chain; for name in chain(vip_list, guest_list): print(name)
2015年

14
@ user16547我们称这些为“软件开发人员”!
布赖恩

6
从蒂姆·彼得斯(Tim Peters)的Python禅宗:Special cases aren't special enough to break the rules. Although practicality beats purity.
泽农2015年

3
还考虑是否要“潮湿的代码”或“干燥的代码”,请参见stackoverflow.com/questions/6453235/…–
Ian

Answers:


201

尽管这是决定拆分功能的一个因素,但是重复某件事的次数不应是唯一的因素。为只执行一次的对象创建函数通常很有意义。通常,您希望在以下情况下拆分功能:

  • 它简化了每个单独的抽象层。
  • 您为分离函数使用了很好的有意义的名称,因此通常不需要在抽象层之间跳转就可以了解发生了什么。

您的示例不符合该标准。您正在从一种模式转换为一种模式,并且从清晰度的角度来看,名称实际上并没有给您带来任何好处。话虽如此,简单的功能在教程和学校作业之外很少见。大多数程序员倾向于以其他方式犯错误。


这就是我在罗伯特·哈维的答案中想念的重点。
布朗

1
确实。我经常将复杂的函数拆分为内联的子例程-不会出现性能下降,但很干净。匿名作用域也很好。
imallett

3
还有一个范围问题。它可能没有意义写print_name(NAME_LIST)函数,整个班级或整个模块可以看到,但它可能(仍然在这种情况下拉伸),应使函数内的本地函数来清理几行重复的代码。
约书亚·泰勒

6
我可能要补充的另一点是标准化。在第一个示例中,正在做一个问候语,将其粘贴在一个函数中以确保您对两个人的问候语相同是很有意义的。特别是考虑到问候语将来可能会改变:)
Svish

1
仅仅为+1为只执行一次的函数创建函数通常很有意义。曾经有人问我如何设计用于模拟火箭发动机行为的功能的测试。该函数的循环复杂性是在90年代,在某些情况下这不是一个switch语句(很丑,但是可​​以测试)。相反,它是由工程师编写的令人费解的混乱,完全无法测试。我的回答是,它是无法测试的,需要重写。他们听了我的建议!
David Hammen 2015年

104

仅当复制是有意的而不是偶然的

或者,换一种说法:

仅当您期望它们在未来共同发展时。


原因如下:

有时,即使彼此无关,两段代码也恰好变得相同。在那种情况下,您必须抵制将它们组合在一起的冲动,因为下次有人对其中一个进行维护时,该人将不会期望所做的更改会传播到以前不存在的调用者,因此该功能可能会中断。因此,您仅在有意义时才将代码分解出来,而不必在似乎减小代码大小的情况下考虑。

经验法则:

如果代码仅返回新数据且不修改现有数据或具有其他副作用,那么将其作为单独的函数分解很可能是安全的。(我无法想象在没有完全改变函数的预期语义的情况下会导致损坏的任何情况,这时函数名称或签名也应更改,在这种情况下,您需要格外小心。 )


12
+1我认为这是最重要的考虑因素。没有人重构代码期望他们的更改是最后的更改,因此请考虑您的更改将如何影响代码中的未来更改。
yoniLavi 2015年

2
它是红宝石,但此来自Avdi Grimm的关于巧合复制的截屏视频仍然与以下问题有关:youtube.com/watch?
DGM

60

不,并非总是最佳做法。

在所有其他条件相同的情况下,线性,逐行代码比在函数调用周围跳转更容易阅读。一个非平凡的函数调用总是带有参数,因此您必须对所有这些参数进行分类,并使从函数调用到函数调用的思维上下文跳跃。除非您有充分的理由(例如获得必要的性能改进),否则始终希望更好的代码清晰度。

那么为什么将代码重构为单独的方法呢? 改善模块化。 收集单个方法背后的重要功能并为其赋予一个有意义的名称。如果您没有做到这一点,那么您就不需要那些单独的方法。


58
代码的可读性是最重要的。
汤姆·罗宾逊

27
如果您没有忘记创建函数的首要原因之一,那么我会赞成这个答案:通过给功能块起一个好名字来创建抽象。如果您添加了这一点,很显然线性的逐行代码并不总是比使用格式正确的抽象的代码更容易阅读。
布朗

17
我同意布朗博士的观点,代码不一定比正确抽象时逐行可读。好的函数名称使高级函数非常易于阅读(因为您正在阅读意图而不是实现),而低级函数则易于阅读,因为您执行的是一项任务,仅执行一项任务,仅执行该任务。这使功能简洁而精确。
乔恩·斯托利

19
Why take the performance hit of a function call when you don't get any significant benefit by doing so?表现如何?在一般情况下,小型函数会内联,大型函数的开销可以忽略不计。CPython可能不会内联,但是没有人使用它来提高性能。我也对此有疑问line by line code...。如果您想在大脑中模拟执行,则会更容易。但是调试器会做得更好,尝试通过遵循循环的执行来证明循环的某些事情就像通过遍历所有整数来证明有关整数的语句一样。
Doval 2015年

6
@Doval提出了一个非常有效的观点-编译代码时,该函数被内联放置(并重复了),因此,尽管编译过程中很小,但运行时性能不会受到影响。对于解释型语言而言并非如此,但是即使如此,函数调用也只占执行时间的一小部分。
乔恩·斯托利

25

在您的特定示例中,执行功能可能看起来过于矫kill过正。相反,我会问一个问题:这种特殊的问候语将来可能会改变吗?怎么会这样?

函数不仅用于包装功能并简化重用,而且还易于修改。如果需求发生变化,复制粘贴的代码将需要手动查找和更改,而具有此功能的代码只需修改一次。

您的示例将从中受益,而不是通过函数受益-因为逻辑几乎不存在-可能是GREET_OPENING常量。然后可以从文件中加载此常量,以便您的程序可以轻松地适应例如不同的语言。请注意,这是一个粗略的解决方案,一个更复杂的程序可能需要一种更完善的方式来跟踪i18n,但这仍然取决于需求与工作量。

最终,一切都与可能的需求有关,并提前计划事情以减轻将来的工作负担。


使用GREET_OPENING常量假定所有可能的问候都以相同的顺序进行。嫁给母语几乎与英语相反的人,使我对这种假设持怀疑态度。
罗兰·佩希特尔

22

就我个人而言,我采用了三个规则-我将通过致电YAGNI来证明这一点。如果我需要编写一次代码,那么我可以复制/粘贴两次(是的,我刚刚承认要复制/粘贴!),因为我不再需要它,但是如果我需要这样做再说一遍,然后我将重构并将其提取到自己的方法中,我向自己证明,我再次需要它。

我同意卡尔·比勒费尔特(Karl Bielefeldt)和罗伯特·哈维(Robert Harvey)所说的话,而我对他们所说的话的解释是,首要原则是可读性。如果它使我的代码更易于阅读,则可以考虑创建一个函数,请记住DRYSLAP之类的东西。如果我可以避免在函数中更改抽象级别,那么我会发现这很容易管理,因此不要在函数之间跳转(如果我无法仅通过读取函数名称来了解函数的功能),则意味着我的开关更少心理过程。
同样,不必在函数和内联代码(例如print "Hi, Tom"对我有用的代码)之间切换上下文,在这种情况下,我可以提取一个函数PrintNames() iff 我总体功能的其余部分主要是函数调用。


3
c2 Wiki上有一个很好的关于“三规则”的页面:c2.com/cgi/wiki?RuleOfThree
pimlottc

13

它很少是明确的,因此您需要权衡以下选项:

  • 截止日期(尽快修复服务器机房)
  • 代码的可读性(可能会影响选择的任何一种方式)
  • 共享逻辑的抽象级别(与上述相关)
  • 重用要求(即具有完全相同的逻辑很重要,或者现在很方便)
  • 分享困难(这是python闪耀的地方,请参阅下文)

在C ++中,我通常遵循三个规则(即第三次我需要相同的东西时,我将其重构为可适当共享的部分),但是根据经验,随着对范围的了解越来越多,最初更容易做出选择,您正在使用的软件和域。

但是,在Python中,重用(而不是重复)逻辑相当轻巧。国际海事组织比其他许多语言(至少在历史上)更是如此。

因此,请考虑仅在本地重用逻辑,例如通过从本地参数创建列表:

def foo():
    for name_list in (vip_list, guest_list): # can be list of tuples, for many args
        for name in name_list:
            print name

如果需要多个参数,则可以使用元组的元组,并将其直接拆分为for循环:

def foo2():
    for header, name_list in (('vips': vip_list), ('people': guest_list)): 
        print header + ": "
        for name in name_list:
            print name

或制作一个局部函数(也许是您的第二个例子),这使逻辑得以重用,但也清楚地表明在函数外未使用print_name():

def foo():
    def print_name(name_list):
        for name in name_list:
            print name

    print_name(vip_list)
    print_name(guest_list)

函数是首选,尤其是当您需要中途终止逻辑(即使用return)时,因为中断或异常可能会不必要地使事情混乱。

这两种方法都比重复相同的逻辑(IMO)更好,而且比声明仅由一个调用者(尽管两次)使用的全局/类函数要容易得多。


在看到这个答案之前,我对新功能的范围发表了类似的评论。我很高兴有一个针对该问题的答案。
约书亚·泰勒

1
+1-也许这不是OP所寻求的答案,但至少就他所问的问题而言,我认为这是最明智的回答。这种东西可能会以某种方式为您嵌入到语言中。
Patrick Collins

@PatrickCollins:感谢您的投票!:)我添加了一些注意事项以使答案更加完整。
Macke 2015年

9

所有最佳做法都有特定的原因,您可以参考该原因以回答此类问题。当潜在的未来变化也意味着对其他组件的更改时,您应该始终避免波动。

因此,在您的示例中:如果您假设您有一个标准的问候语,并且想要确保所有人都一样,那么请写

def std_greeting(name):
    print "Hi, " + name

for name in ["Tom", "Mary"]:
    std_greeting(name)   # even the function call should be written only once

否则,您必须注意并在两个地方更改标准问候语(如果它发生了变化)。

但是,如果只是偶然地使“ Hi”相同,并且更改一个问候语不一定会导致另一个问候语发生变化,则请将它们分开。因此,如果有合理的理由可以相信,请将它们分开,可以进行以下更改:

print "Hi, Tom"
print "Hello, Mary"

第二部分,决定将多少“打包”到函数中可能取决于两个因素:

  • 保持块较小以提高可读性
  • 保持块足够大,以便仅在几个块中发生更改。无需过多集群,因为很难跟踪调用模式。

理想情况下,发生更改时,您会认为“我必须更改以下大部分代码”,而不是“我必须更改大代码块内某处的代码”。


考虑到您的循环,这只是个愚蠢的选择-如果确实只需要迭代几个硬编码的名称,并且我们不希望它们更改,我认为不使用清单。 for name in "Tom", "Mary": std_greeting(name)
yoniLavi 2015年

好吧,继续说一个硬编码的列表将不会出现在代码中间,并且无论如何将是一个变量:)但是,对,我实际上忘记了可以在此处跳过花括号。
Gerenuk

5

命名常量和函数最重要的方面不是减少它们的键入量,而是它们“附加”了使用它们的不同位置。关于您的示例,请考虑以下四种情况:

  1. 必须以相同的方式更改两个问候语,例如[ Bonjour, TomBonjour, Mary]。

  2. 有必要更改一个问候语,但不要更改其他问候语(例如Hi, TomGuten Tag, Mary)。

  3. 有必要以不同方式更改两个问候语(例如Hello, TomHowdy, Mary)。

  4. 两种问候都不需要更改。

如果永远都不需要更改任何问候语,那么采用哪种方法都没有关系。使用共享功能将具有自然的效果,即更改任何问候语都会以相同的方式更改它们。如果所有的问候都应该以相同的方式更改,那将是一件好事。但是,如果不应该,则每个人的问候都必须单独编码或指定;使它们使用通用功能或规范的任何工作都将被撤消(最好不要一开始就做)。

可以肯定的是,并非总是可以预测未来,但是如果有人有理由相信两种问候语都需要一起改变的话,那将是使用通用代码(或命名常量)的一个因素。 ; 如果有理由相信一个或两个都可能需要更改以使它们不同,那么这将是尝试使用通用代码的一个因素。


我喜欢这个答案,因为(至少对于这个简单的示例而言),您几乎可以通过一些博弈论来计算选择。
RubberDuck

@RubberDuck:我认为在什么方面应该“不应该混淆”的问题通常没有得到太多关注,在很多方面,我希望错误的两个最常见原因是(1)认为事情是不在时使用别名/附加,或者(2)更改某些东西而没有意识到其他事物已附加到它。在代码和数据结构的设计中都会出现这样的问题,但是我不记得曾经对这个概念给予过多关注。一般情况下,只有某件事中的某一项,或者它永远不会改变,否则混叠并不重要,但是……
supercat

@RubberDuck:...如果有两个匹配并且它们匹配,那么重要的是要知道更改一个是否应该使另一个值保持不变,或保持关系不变(这意味着另一个值会改变)。我经常看到保持价值不变的优势很受重视,但是关系的重要性却远没有得到重视。
超级猫

我完全同意。我只是想弄清楚传统知识是如何将其干燥的,但是从统计学上讲,重复的代码以后将不再是重复的代码的可能性更大。您基本上有2比1的赔率。
RubberDuck

@RubberDuck:我给出了两种情况,其中分离是重要的,而其中附着性更好的是,它并没有说明各种情况发生的相对可能性。重要的是要识别两段代码是“巧合”匹配的,还是因为它们具有相同的基本含义。此外,当存在两个以上的项目时,还会出现其他情况,其中一个最终要使一个或多个与另一个有所不同,但仍然以相同的方式更改两个或多个。同样,即使一个动作只在一个地方使用...
supercat

5

我认为您应该有理由创建一个程序。创建过程有多种原因。我认为最重要的是:

  1. 抽象
  2. 模块化
  3. 代码导航
  4. 更新一致性
  5. 递归
  6. 测试

抽象化

可以使用一个过程从算法中特定步骤的细节中提取通用算法。如果我能找到一个适当的连贯抽象,这将帮助我构建思考算法功能的方式。例如,我可以设计一种适用于列表的算法,而不必考虑列表表示形式。

模块化

过程可用于将代码组织到模块中。模块通常可以单独处理。例如,分别构建和测试。

设计良好的模块通常会捕获一些连贯的有意义的功能单元。因此,它们通常可以由不同的团队拥有,可以完全替换为其他团队,也可以在不同的上下文中重复使用。

代码导航

在大型系统中,查找与特定系统行为关联的代码可能具有挑战性。库,模块和过程中代码的分层组织可以帮助应对这一挑战。特别是如果您尝试a)以有意义且可预测的名称组织功能b)将与类似功能相关的过程彼此靠近。

更新一致性

在大型系统中,找到实现特定行为更改所需的所有代码都是很困难的。使用过程来组织程序的功能可以使此过程更容易。特别是,如果程序的每个功能仅出现一次并且在一组紧密的过程中出现,那么(根据我的经验),您不太可能会错过应该更新的地方或进行不一致的更新。

请注意,您应该根据程序的功能和抽象来组织过程。并非基于此时两个代码是否正好相同。

递归

使用递归要求您创建过程

测试中

通常,您可以相互独立地测试过程。独立测试过程主体的不同部分更加困难,因为通常必须在第二部分之前执行过程的第一部分。此观察通常还适用于其他指定/验证程序行为的方法。

结论

这些要点中的许多都与程序的可理解性有关。您可能会说,过程是一种创建特定于您的域的新语言的方法,通过该语言可以组织,编写和阅读有关域中的问题和流程的信息。


4

您给出的两种情况都不值得重构。

在这两种情况下,您所执行的操作都必须清楚而简洁地表达,并且不存在进行不一致更改的危险。

我建议您始终在以下位置寻找隔离代码的方法:

  1. 您可以将一项可识别的,有意义的任务分离出来,并且

  2. 要么

    一种。该任务很难表达(考虑到您可以使用的功能),或者

    b。该任务已多次执行,您需要一种方法来确保在两个地方都以相同的方式对其进行更改,

如果不满足条件1,那么您将找不到正确的代码接口:如果尝试强制执行该代码,则最终将加载大量参数,要返回的多个内容,大量上下文逻辑,并且很可能是找不到好名字。尝试记录一下您首先想到的接口,这是检验以下假设的一种好方法:该功能是正确的捆绑,并附带了一个额外的好处,即在编写代码时,已经对其进行了规范和记录!

2a可能没有2b:有时即使我知道它只被使用过一次,有时我也会从代码中取出代码,这仅仅是因为将其移到别处并用一行替换它意味着调用它的上下文突然变得更加可读(特别是如果这是一个简单的概念,该语言恰巧难以实现)。当读取提取的函数时,逻辑的起点和终点以及其作用也很清楚。

没有2a,2b是可能的:诀窍在于感觉哪种变化更有可能发生。如果您发送付款要求或服务中断道歉,您的公司政策很有可能会从始终到处打“ Hi”变为“ Hello”,或者打招呼变成更正式的语气?有时可能是因为您不确定使用外部库,并希望能够快速将其交换为另一个库:即使功能未更改,实现也可能会更改。

不过,大多数情况下,您会混合使用2a和2b。而且,您必须运用自己的判断力,考虑到代码的商业价值,代码的使用频率,是否经过充分的文档编制和理解,测试套件的状态等。

如果您发现相同的逻辑被多次使用,那么您绝对应该趁此机会考虑重构。如果您n在大多数语言中开始的新语句要比缩进级别更多,那么这是另一个稍微不那么重要的触发器(n请为您的给定语言选择一个您满意的值:例如,Python可能为6)。

只要您考虑这些事情并清除掉大型意大利面条代码怪兽,您就应该没事-不要花太长时间担心您的重构需要多少粒度,因为您没有时间测试或文档编制工作。如果确实需要做,时间会证明一切。


3

总的来说,我同意罗伯特·哈维(Robert Harvey)的观点,但是我想添加一个案例,以将功能分解为功能。提高可读性。考虑一种情况:

def doIt(smth,smthElse)
    for x in getDataFromSomething(smth,smthElse):
        if not check(x,smth):
            continue
        process(x,smthElse)
        store(x) 

即使未在其他任何地方使用这些函数调用,但是如果所有三个函数都相当长且具有嵌套循环等,则可读性也会大大提高。


2

没有必须应用的严格规则,这取决于将要使用的实际功能。对于简单的姓名打印,我不会使用函数,但是如果它是一个数学和,那就是另一回事了。然后,即使它仅被调用两次,您也将创建一个函数,这是为了确保数学和在发生变化时始终相同。在另一个示例中,如果您执行任何形式的验证,则将使用一个函数,因此在您的示例中,如果您需要检查名称长于5个字符,则可以使用一个函数来确保始终执行相同的验证。

因此,当您说“对于需要两行以上的代码,我必须写一个函子”时,我认为您回答了自己的问题。通常,您将使用函数,但还必须使用自己的逻辑来确定使用函数是否会带来任何类型的增值。


2

我已经相信这里没有提到过关于重构的一些东西,我知道这里已经有很多答案了,但是我认为这是新的。

自从条款出现之前,我一直是无情的重构者和DRY的坚定拥护者。这主要是因为我难以保持较大的代码库,部分原因是我喜欢DRY编码,而对C&P编码则一无所知,实际上这对我来说是痛苦的,而且非常缓慢。

问题是,坚持使用DRY在一些我很少见到的其他技术上给了我很多实践。许多人坚持认为Java很难或不可能进行DRY,但实际上他们只是不尝试。

很久以前的一个例子与您的例子有些相似。人们倾向于认为Java GUI的创建很困难。当然是这样的代码:

Menu m=new Menu("File");
MenuItem save=new MenuItem("Save")
save.addAction(saveAction); // I forget the syntax, but you get the idea
m.add(save);
MenuItem load=new MenuItem("Load")
load.addAction(loadAction)

任何认为这太疯狂的人都是绝对正确的,但这不是Java的错-绝不应该这样编写代码。这些方法调用是旨在包装在其他系统中的函数。如果找不到这样的系统,请构建它!

您显然不能像这样编写代码,因此您需要退后一步看看问题,重复的代码实际上在做什么?它指定一些字符串及其关系(一棵树),并将该树的叶子连接到动作。因此,您真正想要的是说:

class Menu {
    @MenuItem("File|Load")
    public void fileLoad(){...}
    @MenuItem("File|Save")
    public void fileSave(){...}
    @MenuItem("Edit|Copy")
    public void editCopy(){...}...

一旦您以简洁明了的方式定义了关系,就编写了一种处理该关系的方法-在这种情况下,您遍历传入的类的方法并构建一棵树,然后使用该树来构建菜单和操作以及(显然)显示菜单。您将没有重复,并且您的方法是可重用的……与许多菜单相比,它可能更容易编写,而且,如果您实际上喜欢编程,那么您会感到很多乐趣。这并不难-您需要编写的方法可能比手工创建菜单所需的行少!

事情是,为了做到这一点,您需要练习很多。您需要善于准确分析重复部分中的独特信息,提取该信息并弄清楚如何很好地表达它。学习使用诸如字符串解析和注释之类的工具会很有帮助。学会弄清错误报告和文档记录也很重要。

只需编写良好的代码,即可获得“免费”练习-碰巧的机会是,一旦您熟练掌握了代码,就会发现对DRY进行编码(包括编写可重用的工具)比复制和粘贴以及所有错误,重复错误更快以及编码类型造成的困难变化。

如果我没有尽可能多地练习DRY技术和构建工具,我认为我将无法享受工作。如果我不得不削减工资而不必进行复制和粘贴编程,那我会接受的。

所以我的观点是:

  • 除非您不知道如何正确重构,否则“复制粘贴”将花费更多时间。
  • 您会通过这样做来学会重构,即使在最困难和琐碎的情况下也要坚持使用DRY。
  • 您是一名程序员,如果您需要一个小的工具来使您的代码DRY,请对其进行构建。

1

通常,将某物拆分成一个函数可以提高代码的可读性是一个好主意,或者,如果经常重复的部分在某种程度上是系统的核心。在上面的示例中,如果您需要根据区域设置来向用户打招呼,那么拥有单独的打火机功能将很有意义。


1

作为@DocBrown对@RobertHarvey使用函数创建抽象的评论的必然推论:如果您无法提供一个信息丰富的函数名称,其名称远不比其背后的代码更简洁或更清晰,那么您就没有太多抽象。缺少使其具有功能的其他任何良好理由,则无需这样做。

另外,函数名称很少能完整地表达其语义,因此,如果它不常用,那么您可能必须检查一下它的定义。尤其是使用功能语言以外的其他语言时,您可能需要知道它是否具有副作用,可能发生什么错误以及如何对其进行响应,其时间复杂度,是否分配和/或释放资源以及它是否是线程-安全。

当然,根据定义,我们这里只考虑简单的函数,但这是双向的-在这种情况下,内联不太可能增加复杂性。此外,读者可能不会意识到这是一个简单的功能,除非他看上去。即使使用将您超链接到定义的IDE,视觉上的跳跃也是理解的障碍。

一般而言,将代码递归分解为更多,更小的函数将最终使您到达函数不再具有独立含义的地步,因为随着代码片段的编写变得越来越小,这些片段从创建的上下文中获得了更多的含义。根据周围的代码。打个比方,把它想像成一张图片:如果放大得太近,您将看不清正在查看的内容。


1
即使将其包装在函数中是最简单的语句,也应使其更具可读性。有时将其包装在函数中会显得过大,但是如果您不能提供一个比语句所执行的操作更简洁明了的函数名,则可能是一个不好的信号。您对使用函数的否定并不是什么大不了的事。内联的负面影响更大。内联使其难以阅读,难以维护和重用。
2015年

@pllee:我已经介绍了为什么将功能分解到极端时会适得其反的原因(请注意,问题是专门针对极端,而不是一般情况。)您没有对这些观点提出反驳,但仅声称它“应该”有所不同。写出命名问题“可能”是一个不好的信号的说法并不是在这里考虑的情况下可以避免的论点(我的论点当然至少是警告,您可能已经达到了仅仅为自己着想进行抽象。)
sdenham

我提到了3个否定内联的负面因素,即不确定我仅在哪里声称它“应该有所不同”。我要说的是,如果您不能提出一个名字,那么您的内联可能做得太多。另外,我认为您在这里甚至遗漏了即使是很小的一次使用的方法的重要意义。存在微小的命名方法,因此您不必知道代码要执行的操作(无需IDE跳转)。例如,即使`if(day == 0 || day == 6)`vs`if(isWeekend(day))的简单陈述也变得更易于阅读和思维导图。现在,如果您需要重复该声明,则isWeekend变得毫不费力。
pllee

@pllee如果您无法为几乎每个重复的代码片段都找到一个明显更短,更清晰的名称,则您认为这“是一个不好的信号”,但这似乎是出于信仰问题-您不提供任何支持性的论点(在这种情况下,您需要的不只是示例。)isWeekend确实有一个有意义的名称,因此它可能无法通过我的标准的唯一方法是,它的长度既不明显也不比其实现明显短。通过争论您认为是后者,您在断言这不是我的立场的反例(FWIW,我使用过isWeekend本人。)
sdenham

我给出了一个示例,其中甚至单行语句也更易读,而我给出了内联代码的反面。那是我的拙见,我只是说说而已,不,我不是在反对或谈论“无法命名的方法”。我不确定是什么使我感到困惑,或者你为什么认为我需要“理论证明”。我以提高可读性为例。维护比较困难,因为如果代码块发生更改,则需要N个点(有关DRY的信息)。除非您认为复制粘贴是重用代码的可行方法,否则显然很难重用。
2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.