每当我发现自己多次编写相同的逻辑时,我通常会将其粘贴在一个函数中,因此在我的应用程序中只有一个地方需要维护该逻辑。副作用是我有时最终会遇到一两个行函数,例如:

function conditionMet(){
   return x == condition;
}


OR

function runCallback(callback){
   if($.isFunction(callback))
     callback();
}


这是偷懒还是不好的做法?我之所以这么问,是因为这会导致大量的函数调用需要非常小的逻辑。

评论

不,不要太短。在C#中,我将“满足条件”作为一个优雅的属性,并考虑使用Assert.AreEqual (期望,实际,消息,arg1,arg2,arg3,...);第二个就可以了。我可能会包含一个可选的bool标志,该标志将指示是否抛出异常/等。万一回调不是函数。

是的,仅在一种情况下:function myFunction(){}

def yes():返回“是”

@Spooks-我在这里分裂头发,但空函数至少对适配器类有效,例如download.oracle.com/javase/6/docs/api/java/awt/event/…中的MouseMotionAdapter。当然,这只是解决语言限制的一种方法。

@dietbuddha:要求刚刚更改-现在下周的演示必须支持两种语言。编写“ def yes()”的人感到非常高兴,然后将其更改为“ def yes(:(IsFrench()?'Oui':'Yes')“)

#1 楼

呵呵,布朗先生,如果我能说服我遇到的所有开发人员将其功能保持在这种程度之内,相信我,软件世界将是一个更好的地方!

1)您的代码可读性增加10倍。

2)由于可读性,因此很容易弄清楚代码的过程。

3)干-不要重复自己-您是

4)可测试。微小的功能比我们经常看到的200多种方法要容易测试一百万倍。

不必担心性能上的“功能跳跃”。 “发布”构建和编译器优化为我们很好地解决了这一问题,并且性能是系统设计中其他任何地方的99%。

这很懒吗? -恰恰相反!

这是不好的做法吗? - 绝对不。比起太常见的tar球或“上帝对象”,最好采用这种制作方法。

跟我的工匠保持出色的工作;)

评论


您不是在回答问题,而是在宣讲在这里质疑更多极端应用的规则。

–user7043
2011年4月1日19:29



我很少会发现只有1个upvote令人沮丧的局限性。但是,对于这个答案,+ 1似乎并不公平。

–贝文
2011年4月1日19:52

+5 ...哦只能做+1,哦!为了支持MeshMan,我建议以下由Robert C. Martin Clean编写的书。这本书确实在改变我现在编程的方式。

–埃里克·卡尔
2011年4月2日在16:40



非常讨人喜欢的答案,但我不同意其中的大部分:1)极小的函数会导致整体上产生更多的代码,因为每个函数都必须进行所有的检查。这可能会降低可读性,尤其是对于一个非常大的项目。而且,取决于语言。 2)完全取决于点1,因此与问题无关。 3)什么? runCallback重复四次“回调”,并且三遍引用同一变量以运行单个函数。 4)当然,无法测试200多个行的方法,但是从这一方法到一条行还有很长的路要走。

–l0b0
2011年6月9日14:04



-1:这里最令人恐惧的是对此答案的否决次数。

–路卡
2012年6月6日21:39

#2 楼

我要说的是,重构方法太短了:


它复制了原始操作,仅出于使它成为方法的目的:

例如:

boolean isNotValue() {
   return !isValue();
}


或...


该代码仅使用一次,其意图在以下位置易于理解一目了然。

Ex:

void showDialog() {
    Dialog singleUseDialog = new ModalDialog();
    configureDialog(singleUseDialog);
    singleUseDialog.show();
}

void configureDialog(Dialog singleUseDialog) {
    singleUseDialog.setDimensions(400, 300);
}


这可能是有效的模式,但是我只需要在此内联configureConfig()方法例如,除非我打算重写它或在其他地方重用此代码。

评论


在第二种情况下,分隔单行方法可能有利于保持调用方法抽象级别的一致性。如果使用此推理,则提供的示例会显示在栅栏上:确切的像素宽度和高度是否过多,无法显示对话框?

– Aleksi Yrttiaho
2011年4月2日,下午5:34

“ isNotValue()”方法的名称很错误,应进行重构以命名实际的域问题。

–user1249
2011年4月2日,12:34

@Aleksi:我不同意;在此示例中,详细信息已隐藏在showDialog()方法中;无需对其进行双重重构。该示例旨在显示一个孤立的用例;如果在不同情况下需要使用不同对话框配置的模式,则在模式建立后返回并重构是有意义的。

–RMorrisey
2011年4月4日在22:30

@Thorbjorn:当您回答一个业务逻辑不清晰的业务领域问题时,我可以看到一个案例。我只是指出,在某些情况下,它是过大的。

–RMorrisey
2011年4月4日22:32

也许这是挑剔的,但我会争辩说,在您的示例中,问题并不在于功能太短,而在于它们没有增加描述性的价值。

–keppla
2011年6月9日15:12

#3 楼

函数可以太短吗?一般没有

实际上,确保以下内容的唯一方法:


您已经找到了设计中的所有类
您的函数只做一件事。

是为了使您的功能尽可能小。或者,换句话说,从函数中提取函数,直到无法提取为止。我称其为“提取直到删除。”要解释这一点:函数是具有通过变量进行通信的功能块的作用域。类也是具有通过变量进行通信的功能块的作用域。因此,长函数总是可以用小方法替换为一个或多个类。

另外,一个足够大的函数允许您从中提取另一个函数,根据定义,它所做的不只是一件事情。因此,如果可以从另一个函数中提取功能,则应该提取该函数。

有些人担心这会导致功能的泛滥。他们是对的。它会。这实际上是一件好事。很好,因为函数有名称。如果您在选择好名字时很小心,则这些功能可以充当指示其他人通过您的代码的路标。的确,在命名空间中,在命名类中的命名函数是确保读者不会迷路的最​​佳方法之一。

在cleancoders.com上的Clean Code第三章中有很多关于此的内容

评论


鲍伯叔叔!很高兴在这里见到您。如预期的那样,很棒的建议。我之前从未听过“抽空直到您掉线”一词,并且可以肯定的是星期一在办公室使用这个词;)。

–马丁·布洛(Martin Blore)
2011年4月2日,0:40

您愿意阐述您的第一点吗?以身作则解释会很棒。

–丹尼尔·卡普兰(Daniel Kaplan)
13年2月7日在23:11

#4 楼

哇,这些答案中的大多数根本不是很有帮助。

不应编写任何以其定义为身份的函数。也就是说,如果函数名称只是用英语写出的函数代码块,则不要将其写为函数。

请考虑您的函数conditionMet和另一个函数addOne(请原谅我生疏的JavaScript):

function conditionMet() { return x == condition; }

function addOne(x) { return x + 1; }


conditionMet是正确的概念定义; addOne是重言式。 conditionMet很好,因为您只说“满足条件”就不知道conditionMet意味着什么,但是如果您用英语阅读,您就会明白为什么addOne很愚蠢:

"For the condition to be met is for x to equal condition" <-- explanatory! meaningful! useful!

"To add one to x is to take x and add one." <-- wtf!


出于对任何可能仍然圣洁的事物的热爱,请不要编写重言式功能!

(出于同样的原因,请不要为每行代码写注释!)

评论


一些较复杂的语言构造可能不熟悉或难以阅读,这使之成为有用的技巧。

–user1249
2011年4月2日在12:37

商定,要确切地定义为一个函数的功能很大程度上取决于它所使用的语言。如果您使用的是VHDL之类的语言,而您实际上定义的是在一个语言中添加含义,那么addOne之类的功能将非常有用。二进制或数论级别。我想认为没有一种语言具有如此神秘的含义,即使是经验丰富的程序员也很难阅读(也许是brainf#ck),但是如果是这种情况,由于该功能实际上是英语的翻译,因此会出现同样的想法该语言的名称-因此名称与定义不同。

–宫阪丽
2011年4月2日在21:03

我说的是Haskell标准库中的事物身份功能-我认为您不能从中获得更多的“重言式” :)

–hugomg
2011年11月12日在3:22



@missingno Bah,那是作弊:D

–宫阪丽
2011年11月12日下午4:13

如果您根据功能的目的而不是内容来命名功能,它们将立即变得愚蠢。所以你的例子可能是是函数nametoolong(){返回name.size> 15; }或函数successorIndex(x){return x + 1; }因此,您的函数的问题实际上只是它们的名称不好。

–汉斯·彼得·斯托尔
2012年1月2日19:06



#5 楼

我想说的是,如果您认为可以通过添加注释来改善某些代码的意图,则可以将代码提取到自己的方法中,而不是添加注释。不管代码多么小。

例如,如果您的代码看起来像这样:

if x == 1 { ... } // is pixel on?


使它看起来像这样而是:

if pixelOn() { ... }




function pixelOn() { return x == 1; }


换句话说,这与方法的长度无关,而是关于自我记录代码。

评论


我尊敬的同事指出,如果x == PIXEL_ON,您也可以写。使用常数可以像使用方法一样具有描述性,并且要简短一些。

–汤姆·安德森(Tom Anderson)
2011年6月9日15:28

#6 楼

我认为这正是您想要做的。现在,该功能可能只有一两行,但随着时间的流逝它可能会增长。还有更多的函数调用,使您可以阅读函数调用并了解其中发生的情况。这使您的代码非常干燥(不要重复自己),更易于维护。

评论


那么,当您可以证明自己需要而不是现在只是当自重时,将其分解出来又有什么问题呢?

–dsimcha
2011年4月2日在20:03

函数不会自行增长。恕我直言,例子很糟糕。 conditionMet是一个太通用的名称,并且不带参数,因此它测试某些状态吗? (x == xCondition)在大多数语言和情况下都具有与'xConditition'一样的表现力。

–用户未知
2011年6月9日,1:11

是的,这就是为什么您希望代码中的开销超过75%?单线书写为3行实际上是300%。

–编码器
2011年6月9日在13:28

#7 楼

我同意我所看到的所有其他帖子。这是很好的风格。

这种小方法的开销可能为零,因为优化程序可以优化调用并内联代码。像这样的简单代码可以使优化器尽其所能。

代码应清晰明了地编写。我尝试将方法限制为以下两个角色之一:决策;或执行工作。这可能会生成一种方法。我做得越好,我的代码就越好。

这样的代码往往具有较高的内聚性和较低的耦合度,这是良好的编码实践。

编辑:关于方法名称的注释。使用方法名称,该名称指示什么方法不执行该操作。我发现verb_noun(_modifier)是一个很好的命名方案。这将提供类似于Find_Customer_ByName的名称,而不是Select_Customer_Using_NameIdx。修改方法后,第二种情况容易变得不正确。在第一种情况下,您可以换出整个Customer数据库实施。

评论


+1表示内联,imo在性能和短函数方面是主要考虑因素。

–加勒特·克拉伯恩(Garet Claborn)
2011年4月2日,下午3:23

#8 楼

将一行代码重构为一个函数似乎是多余的。可能会有特殊情况,例如ver loooooong / comples行或expessions,但是除非我知道该功能将来会增长,否则我不会这样做。

您的第一个示例暗示了使用全局变量(可能会或可能不会提及代码中的其他问题),我将对其进行进一步的重构,并将这两个变量作为参数:

function conditionMet(x, condition){
   return x == condition;
}
....
conditionMet(1,(3-2));
conditionMet("abc","abc");


conditionMet如果条件长期且重复,例如:

function conditionMet(x, someObject){
   return x == ((someObject.valA + someObject.valB - 15.4) / /*...whole bunch of other stuff...*/);
}


评论


他的第一个例子并没有暗示全局变量。如果有的话,它是高度内聚类中的一种内聚方法,其中大多数变量都在对象上。

–马丁·布洛(Martin Blore)
2011年4月1日19:34

该conditionMet函数只是一个冗长的==运算符。这可能毫无用处。

–user7043
2011年4月1日19:34

我绝对不使用全局变量。 @MeshMan,这就是示例的确切位置……类上的方法。

–马克·布朗
2011年4月1日19:36



@Mark Brown:@MeshMan:好的,我想代码示例太含糊了,无法确定...

–FrustratedWithFormsDesigner
2011年4月1日19:40在

我闻到conditionMet(x,关系条件){在这里,您传递x,'=='和关系。然后,您无需重复代码“ <”,“>”,“ <=”,“!=”等。另一方面,大多数语言都将这些结构内置在操作符(x ==条件)中。原子语句的功能还差一步。

–用户未知
2011年6月9日在1:18

#9 楼

考虑一下:

一个简单的碰撞检测功能:

bool collide(OBJ a, OBJ b)
{
    return(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) <= pow(a.radius + b.radius, 2));
}


如果您一直在代码中写“简单”的衬里,您可能最终会犯错。另外,一遍又一遍地写它真是很痛苦。

评论


如果我们在这里处理C,我们可能会滥用#define;)

–科尔·约翰逊(Cole Johnson)
2014年5月9日,0:07

如果您的尺寸或距离变得非常大,您甚至可能希望将其换成较慢但防溢出的功能。显然,这一次容易得多。

–马特·克劳斯(Matt Krause)
2014年5月9日,3:58

#10 楼

不,这很少有问题。现在,如果有人觉得任何功能都不能超过一行代码(如果只是那么简单),那将是一个问题,并且在某种程度上是懒惰的,因为他们没有考虑什么是合适的。

评论


我不会在代码行中计算它,而是在表达式中计算它。 “(x == condition)”是原子的,因此,如果多次使用它并且可能对其进行更改,则我只会将其放入方法/函数中,例如function isAdult()= {age> = 18;但肯定不是isAtLeast18。

–用户未知
2011年6月9日在1:20

#11 楼

我会说它们太短了,但这是我的主观意见。

因为:


如果只使用一次,就没有理由创建一个函数或两次。跳到防御吸吮。尤其是使用速度惊人的VS和C ++代码。
类概述。当您有成千上万的小功能时,它使我生气。当我可以查看类定义并快速查看它的功能时,而不是SetXToOne,SetYToVector3,MultiplyNumbers,+ 100个setter / getter的用法时,我感到很高兴。
在大多数项目中,这些助手在一个或两个重构阶段后就变得无用了,并且然后您执行“全部搜索”->删除操作以删除过时的代码,通常占代码的25%以上。

长的函数是不好的,但短于3行并执行的函数只有1件事同样不好,恕我直言。

所以我只说写小函数,如果是的话:初级开发人员可能会错过的东西(不知道)
是否进行了额外的验证
,或者将使用至少3x的内容
简化了常用的界面
在下一次使用时不会成为负担重构
有一些特殊的含义,例如模板专业化或某些事情
做一些隔离工作-const引用,影响可变参数,私有成员检索

我敢打赌下一个开发者( SE nior)比记住所有SetXToOne函数要好得多。因此,无论哪种方式,它们很快都会变成自重。

#12 楼

我不喜欢这个例子。 1,因为第i个通用名称。

conditionMet似乎不是通用的,因此代表特定条件吗?像

isAdult () = { 
  age >= 18 
}


这样就可以了。这是语义上的差异,而

isAtLeast18 () { age >= 18; } 


对我来说不合适。

也许它经常使用,并且可以接受以后的更改:

getPreferredIcecream () { return List ("banana", "choclate", "vanilla", "walnut") }


也很好。多次使用它,如果需要的话,您只需要更改一个位置-也许明天就可以使用生奶油了。

isXYZ (Foo foo) { foo.x > 15 && foo.y < foo.x * 2 }


不是原子的,应该给您是一个不错的测试机会。

当然,如果您需要传递函数,则传递您喜欢的任何东西,然后编写看起来很愚蠢的函数。

但是,总的来说,我看到的函数太多了,而太短的函数却太多了。

最后一句话:有些函数看起来太恰当,因为它们写得太冗长:

function lessThan (a, b) {
  if (a < b) return true else return false; 
}


如果看到的话,那就是与

return (a < b); 

相同

您不会遇到

localLessThan = (a < b); 


而不是

localLessThan = lessThan (a, b); 


#13 楼

没有代码,就没有代码!

保持简单,不要使事情复杂化。

这不是偷懒,它正在完成您的工作!

#14 楼

是的,可以使用短代码功能。如前面提到的那样,在使用方法“ getters”,“ setters”,“ accesors”的情况下很常见。

有时,那些简短的“ accesors”函数是虚拟的,因为在子类中重写时,函数将包含更多代码。

如果您不希望函数这么短,那么在许多函数,全局或方法中,我通常使用“结果”变量(pascal样式)而不是直接函数返回,使用调试器时非常有用。

function int CalculateSomething() {
  int Result = -1;

   // more code, maybe, maybe not

  return Result;
}


#15 楼

太短永远都不是问题。功能短的一些原因是:

可重用性

例如如果您有一个函数(如set方法),则可以断言它以确保参数有效。必须在设置了变量的所有位置进行此检查。

可维护性

您可能会使用一些您认为将来可能会更改的语句。例如。您现在在列中显示一个符号,但稍后可能会更改为其他符号(甚至发出哔声)。

均匀性

您正在使用例如外观模式,函数唯一要做的就是将函数准确地传递给另一个函数而无需更改参数。

#16 楼

当给代码段命名时,本质上是为它命名。这可能是由于多种原因造成的,但关键是要给它一个有意义的名称,该名称可以添加到程序中。诸如“ addOneToCounter”之类的名称不会添加任何内容,但会添加conditionMet()

如果您需要规则来确定代码段太短或太长,请考虑花多长时间才能找到片段的有意义的名称。如果您无法在合理的时间内完成,则该代码段的大小不合适。

#17 楼

否,但是可能太简洁了。

请记住:代码只写一次,但是读了很多遍。

不要为编译器编写代码。为将来必须维护您的代码的开发人员编写。

#18 楼

是的,亲爱的,功能可能会越来越小,它的好坏取决于您使用的语言/框架。

我认为我主要从事前端技术工作,小功能是通常用作辅助函数,使用小型过滤器并在整个应用程序中使用相同的逻辑时,您将不得不像在很多情况下一样使用它们。如果您的应用程序有太多的通用逻辑,那么就会有大量的小功能。

但是在没有通用逻辑的应用程序中,您将不一定会制作小功能但是您可以将代码分成易于管理和理解的段。

通常,将巨大的代码分解为小函数是一种很好的方法。在现代框架和语言中,您一定会这样做,例如

data => initScroll(data)


是ES 2017 JavaScript和Typescript中的匿名函数

getMarketSegments() {
 this.marketService.getAllSegments(this.project.id)
  .subscribe(data => this.segments = data, error => console.log(error.toString()));
}


在上面的代码中,您可以看到3个函数声明和2个函数调用,这是Angular 4中带有Typescript的简单服务调用。您可以将其视为您的要求

([] 0)
([x] 1)
([x y] 2)


上面是clojure语言中的3个匿名函数

(def hello (fn [] "Hello world"))


>上面的代码是clojure中的一个函数声明

所以是的,FUNCTIONS可以更小,但是如果您具有以下功能,则它是好是坏:

incrementNumber(numb) { return ++numb; }


这样做不是一个好习惯,但是如果您在Angular Framework中像在Angular Framework中那样在HTML标记中使用此功能,如果在Angular HTML模板中不支持Increment或Decrement的话,那会怎么样呢?一直是我的解决方案。

让我们再举一个例子

insertInArray(array, newKey) {
 if (!array.includes(newKey)) {
   array.push(newKey);
 }
}


上面的例子是在Angular HTML模板中播放数组时必须使用的例子。所以有时候您必须创建一些小的函数

#19 楼

我不同意这时给出的几乎答案。

最近我发现一个同事,该同事写着所有的类成员,如下所示:

 void setProperty(int value){ mValue=value; }
 int getProperty() const { return (mValue); }


此在零星的情况下可能是一个很好的代码,但是如果您定义了许多具有许多属性的类,那么肯定不是。我不想这样说,不得编写上述方法。但是,如果最终您将大多数代码写为真实逻辑的“包装器”,那就有问题了。

程序员很可能会错过整体逻辑,担心将来的更改以及重构逻辑。代码。

接口的定义是因为确实需要;确实,如果您需要设置并获取一个简单的属性(没有太多的逻辑,这会使成员更短),那是有可能的。
但是,如果可以用不同的方式分析实际需求,为什么不定义一个更直观的界面?

要真正回答这个问题,当然不是,原因非常明显:应根据需要定义方法/属性/警告。即使是空的虚函数也有理由存在。

评论


添加访问器/更改器方法是有充分理由的,您可以在“担心将来...重构”下进行暗示。但是您也很对,它不会为代码增加价值。既不减少大小(本地或全局),也不增加可读性。在我看来,这说明Java和JavaBeans约定中的语言设计都很差。这是C#(很多方面)成功改进语言以解决自动属性语法这两个问题的一个领域。作为Java程序员,我同意您的基本结构式类的原始风格。

– Anm
2011年4月1日22:52

Getter / setter函数很好,即使它们现在所做的只是读取或写入变量。实际上,您可以想象一个场景,稍后您决定每次该字段更改时在屏幕上打印一个值,或者检查该值是否有效。 (也许它是分数的分母,并且您要检查以确保它不为零。)

–宫阪丽
2011年4月2日在7:33

尽管有有效的用法,但getter和setter却很糟糕。如果有必要使用它们,我认为这是该语言的失败。

–卡森·迈尔斯(Carson Myers)
2011年4月3日在7:25

@Rei:为什么外部类需要检查分母的值?这应该通过Fraction类的divid()函数完成,或者Fraction类应提供isDivisible()来检查空分母。需要吸气剂/吸气剂通常是一种代码气味,说明您的班级耦合度高和/或凝聚力低(即不好)。

– Lie Ryan
2011年6月5日19:38

@李什么?我从来没有说过应该使用外部类来检查分母。我的意思是,外部类可能会偶然将分母设置为0。在设置程序中进行有效性检查的目的是鼓励更早发现使用者代码中的任何错误。提供一个isDivisible()函数可能无法调用也无法实现此目的。以及需要吸气剂/吸气剂究竟如何表明存在高耦合?

–宫阪丽
2011年6月6日在1:54