我讨厌引用付费墙内容,但是这段视频恰好显示了我在说什么。正好在罗伯特·马丁(Robert Martin)的12分钟内看着这个:



并说“我最喜欢做的一件事情就是摆脱无用的牙套”,因为他把它变成了这个。 :



很久以前,在遥远的教育中,我被教导不要这样做,因为它很容易通过添加另一个缩进来引入错误。一直认为它是由if控制的。

为了公平起见,鲍伯叔叔,他将一个长的Java方法重构为微小的小函数,我同意这些函数可读性更高。当他完成更改后,(22.18)看起来像这样:



我想知道是否应该验证删除括号。我已经熟悉了最佳实践。鲍勃叔叔可以在这一点上受到挑战吗?鲍勃叔叔捍卫了这个主意吗?

评论

给它几年时间,也许他也将删除无用的换行符,然后说:“如果(看(某事))说(某事);”实际上将是一行代码;-)

@SteveJessop很高兴得知我要提前几年。 :D没有换行符,就不会有臭名昭著的bug的风险。根据需要添加/删除花括号是额外的开销,但阅读时会节省更多。

Bob叔叔在“清除代码”(第35页)中提出了一些很好的建议。如果if块仅是一行,则没关系。如果需要添加更多行,则应该使用另一个函数,它将if块减少到一行(函数调用)。如果您坚持这一点,那么括号根本就没有关系-完全没有。

Bob肯定是错的一种情况是,是否存在始终保持括号的准则(例如,KDE代码库)。在那种情况下,我认为一致性比可读性更好的代码更好。 (是的,鲍勃(Bob)可能也违反了准则,但在KDE这样的项目中,您有数百名开发人员,其中一些是“业余爱好者”或非专业人士,他们会对代码保持一致的看法,并实际上有助于维护代码。)

他应该返回(page == null)吗? “”:includePage(mode,page);如果他真的那么精简...我一直认为无括号风格很酷,直到我开始专业开发应用程序为止。杂音,可能的错字等。括号一直存在,为您节省了以后需要引入它们的时间和开销。

#1 楼

可读性不是一件小事。

当涉及到用单个方法括起来的括号时,我的想法很复杂。我个人删除它们是为了处理诸如单行返回语句之类的事情,但是实际上遗忘了这些括号确实在我最后工作的地方非常困扰我们。有人在if语句中添加了一行代码,但未同时添加必要的花括号,并且由于它是C语言,因此它使系统崩溃而没有警告。

我从不挑战任何信奉宗教的人,因为他们以后总是使用花括号这么小的惨败。

所以我看到了可读性的好处,但是我敏锐地意识到当您把这些大括号留在外面时会出现的问题。

我不会尝试寻找研究或某人发表的意见。每个人都有一个(就是这样的观点),并且因为这是一个风格问题,所以一个观点几乎与其他观点一样好。自己考虑问题,评估利弊,并下定自己的想法。如果您工作的商店的编码标准可以解决此问题,请遵循该准则。

评论


当压痕消失并产生误导时,我也为这个问题所困扰。

–安迪
16年4月4日,0:47

因为它是C,没有GCC 6的-Wmisleading-indentation。

– wchargin
16年4月4日在5:03

@CandiedOrange:鲍勃叔叔正在挑战既定的教条。

–罗伯特·哈维(Robert Harvey)
16年4月4日在5:11

@RobertHarvey我没说清楚吗?是的,他在挑战教条。他在挑战我的教条。我只是想成为一个好学生,至少要考虑一下。但是鲍伯叔叔是如此有魅力,让我怀疑我是否正在失去客观性。因此,我恳求在喝古德莱酒之前寻求帮助,找到解毒药。

–candied_orange
16年4月4日在5:19

@CandiedOrange:绝对是上下文。那些盲目接受教条的人不会考虑背景。他们仍然在托管语言中使用“仅返回”规则,而该规则已失去其实用性并实际上已成为障碍。

–罗伯特·哈维(Robert Harvey)
16-6-4在19:19



#2 楼

您可以在此处,此处或在任何地方涂上自行车棚的地方找到数种已发布的促销活动或拒绝使用不支撑样式的内容。

远离自行车棚,请记住2014年的OS X / iOS SSL错误?

if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
    goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
    goto fail;


是的,由无括号块“引起” https://www.imperialviolet.org/2014/02/22/applebug.html

首选项可能取决于花括号的样式。如果我不得不写

if (suiteSetup)
{
    includePage(mode, suiteSetup);
}


我可能也倾向于节省空间。但是

if (suiteSetup) {
    includePage(mode, suiteSetup);
}


仅使用一条“额外”行。


我知道你没有问,但是我是否我一个人工作,我是一个异端。如果我删除括号,我更喜欢

if (suiteSetup != null) includePage(mode, suiteSetup);


它与iOS SSL样式的错误没有相同的视觉问题,但比起保存更多的“不必要的”行Bob叔叔确实做过;)如果您习惯它,就会读起来很好,并且在Ruby(includePage(mode, suiteSetup) unless suiteSetup.nil?)中具有主流用法。哦,知道了很多意见。

评论


另外,如果您使用} else [if(...)] {,则不会添加任何额外的行,因此在整个过程中总共只会增加一个额外的行。

–Random832
16年4月4日在5:59

很高兴您提出iOS SSL错误。从那时起,我越来越发现自己在这种情况下使用牙套。

–扎克·利普顿(Zach Lipton)
16-6-4在9:21



关于“ goto fail”错误,值得注意的是,Bob叔叔的编码风格的其他方面也会阻止它,最重要的是,他强烈偏爱极短的方法。首先,他永远不会容忍79行的方法,并且如果该方法被分解为较小的方法(a)导致重复的合并冲突可能永远不会发生,并且(b)该方法中的流程控制更简单可能意味着已经生成了针对无法访问的代码的编译器警告,应该可以避免该问题。

–法律
16年4月4日在16:48

“ goto失败”不是由单行代码块引起的,它是由草率的编程,甚至是更草率的代码审查以及甚至没有一次手动执行代码引起的。

– gnasher729
16年5月5日在7:35

嗯,缺少括号并没有导致该错误。令人震惊的程序员(并且没有任何有意义的代码审查,测试)做到了。通过解雇那些负责任的人来解决问题,而不是束缚其他人的双手!

–轨道轻赛
16-6-5在13:29



#3 楼

鲍勃叔叔对这种错误有很多防御措施,当“总是使用牙套”成为普遍的智慧时,这种错误并不常见:


个人对单行条件的强烈偏爱,因此多行程序脱颖而出并受到额外的审查。
当您插入第二行时,编辑器会自动退出。
他不断运行的整套单元测试。
集成测试。
在合并其代码之前进行同行评审。
由连续集成服务器重新运行所有测试。
由产品质量部门进行测试。

我认为,如果有人向鲍勃叔叔发表挑战,他将对以上几点有很好的反驳。这是提早发现的最简单的错误之一。

评论


我从不担心崩溃,就像我害怕那些无声的失败一样。至少您知道何时发生崩溃。当我看到这些东西时,我的脑后会发麻。当我看到while(true); {break;}时,它的感觉与以前一样。如果确实有一个有效的上下文,那将需要我花费一些时间来克服它。

–candied_orange
16年4月4日在5:05

@LokiAstari是的,它被称为“ Java没有宏”。

– svick
16年4月4日在11:35

以上所有这些实际上是“避免使用与“无用括号”政策相关的失败伤害我的方式”,而不是实际上是“使用“无用括号”政策的理由”。您需要这些事实(特别是#1)的事实应被认为是一种破坏优势而不是优势。

– SJuan76
16年4月4日在12:43

@Jules哦,是的!我在工作中收到或发布的所有产品(至少)具有100%的测试覆盖率,经过同行评审(多次),并且所有业务部门均具有软件质量检查部门。是。我猜您在职业生涯中发现了相同的情况,因为每个企业都有程序员团队而不是单个程序员,并且他们给他们足够的时间来进行他们想做的所有测试。是的,它完美地描述了所有工作场所。当我们确定我们的软件始终随附100%的错误时,为什么还要担心添加一些其他错误?

– SJuan76
16年4月4日在17:59



“这是提早发现的最简单的错误之一。” -这也是通过再输入两个字符来完全避免的最简单的错误之一:

–计算机芯片
16年6月6日在7:15

#4 楼

在大多数情况下,这是个人喜好,但是仍然需要考虑一些事情。

可能的错误

可以说,由于忘记了加载项而导致的错误大括号很少见,据我所知,它们确实偶尔会发生(不要忘了著名的IOS goto失败错误)。因此,我认为这应该是考虑您的代码样式时要考虑的因素(某些工具会警告您误导性缩进,因此它也取决于您的工具链)。

有效代码(看起来像是一个bug)

即使假设您的项目没有遭受此类bug,在阅读代码时,您可能也会看到一些看起来像是bug的代码块-但事实并非如此,循环。

我们从以下内容开始:

if (foo)
    bar();


开发人员添加了有用的注释。

if (foo)
    // At this point we know foo is valid.
    bar();


稍​​后在开发人员上进行扩展。

if (foo)
    // At this point we know foo is valid.
    // This never fails but is too slow even for debug, so keep disabled.
    // assert(is_valid(foo));
    bar();


或添加嵌套块:

if (foo)
    while (i--) {
        bar(i);
        baz(i);
    }


或使用宏:

if (foo)
    SOME_MACRO();


“ ...由于宏可以定义多行代码,因此该宏是否对多行使用do {...} while (0)?它在我们的样式指南中,但我最好还是以防万一!”


上面的示例都是有效的代码,但是代码块中的内容更多,您需要阅读的内容越多,以确保没有任何错误。

也许您的代码风格定义了多行块需要大括号(无论如何,即使它们不是代码) ,但是我已经在生产代码中看到了这些注释。当您阅读它时,会有一个小小的疑问,就是最后编辑这些行的人忘了添加括号,有时我觉得需要仔细检查是否正在按预期的方式工作(尤其是在研究该代码区域的错误时)。 br噪声



在一行中使用括号的一个实际原因是减少diff噪声。

,即更改:

if (foo)
    bar();


收件人:

if (foo) {
    bar();
    baz();
}


...导致条件行在差异中显示为已更改,这会增加一些小但不必要的开销。


这些行会显示就像在代码审查中被更改一样,如果您的差异工具是基于单词的,您可以很容易地看到只有花括号已更改,但这需要花费更多的时间检查行是否根本没有更改。工具支持基于单词的差异,即使使用精美的工具,差异(svn,git,hg等)的显示也会像整行一样显示,有时您可能需要快速查看基于普通行的差异才能查看
注释工具(例如git blame)将显示该行已更改,从而使跟踪行的原点更加容易以找到实际的更改。

它们都很小,并且取决于您在代码审查或跟踪中花费了多少时间来提交更改的代码行。

在diff中增加额外的行更改会带来更明显的不便,它们的可能性更大-hood代码更改将导致合并冲突,需要手动解决。


对此有一个例外,对于单独具有{的代码库-没问题。

如果使用这种样式编写,diff noise参数将不成立:

if (foo)
{
    bar();
    baz();
}


但是这不是如此常见的约定,因此主要增加了完整性的答案(不建议项目使用这种样式)。

评论


我喜欢将diff噪声减少的评论,这是一个定义加。

–马丁·约克
16年6月4日在7:23

如果只有对JSON着迷的每个人都对差异噪声有所考虑!我在看着你,最后一个逗号规则。

–罗马·斯塔科夫
16年5月5日在8:32

h,我没有考虑{自己行行样式}的差异噪声优势。我真的很讨厌这种风格,不喜欢在需要这种风格的项目中工作。也许考虑到这一点,我将不得不以这种方式进行编码,这会减少一些挫败感。代码密度很重要。如果您一次可以在监视器上看到所有功能,则可以更轻松地查看整个内容。我喜欢在函数内单独的代码块之间留空行,以提高可读性(对相关语句进行分组),并且被迫在其他地方浪费空间,使预期的中断变得更弱。

– Peter Cordes
16年5月5日在15:30

@ peter-cordes也不喜欢{-on-its-own-line样式的粉丝,只提供了答案完整性。至于信任使用do {} while(0)的宏,我发现问题在于您几乎可以一直信任它。问题是确实发生了事故,错误可能会因代码审查而漏失……并且可能会引入原本不会存在的错误。

–ideasman42
16年6月6日在1:20



如果我们列出了潜在的错误,则注释掉个别行的功能是另一回事。我正在跟踪一个错误,该错误是由某人盲目注释掉单行if语句的正文引起的。然后,以下语句是有条件执行的,而不是无条件执行的。

– Eldritch奶酪
16年6月6日在12:48

#5 楼

多年前,我被带去调试一些C代码。该错误很难被发现,但最终归结为如下语句:

if (debug)
   foo (4);


结果发现,编写该错误的人将foo定义为一个宏。包含两行代码的宏。当然,只有这两行中的第一行受if的约束。 (因此第二行是无条件执行的。)

这对于C及其预处理器可能是绝对唯一的-在编译之前进行替换-但我从未忘记它。那样的事情会在您身上留下印记,为什么不安全地使用它-尤其是如果您使用多种语言和工具链,并且不能确定在其他地方无法实现这种恶作剧吗?

现在显然,我缩进和使用括号的方式与其他所有人不同。对于单行if,我可以这样做:

if (debug) { foo (4); }


,因此不需要任何其他行即可包含括号。

评论


在那种情况下,谁写那个宏是谁的错。

– gnasher729
16年4月4日在18:37

正确编写C预处理程序宏可能并不直观,但是可以做到。正如gnasher729所指出的,它应该做到。您的示例就是为什么包含多个语句的任何宏都必须在do {...} while(0)循环中包含其主体的原因。这不仅可以确保始终通过包含if()语句来正确对待它,而且还可以确保正确地插入了该语句末尾的分号。谁不知道这样的简单规则,就不应该编写预处理程序宏。在呼叫站点进行防御是错误的防御位置。

–cmaster-恢复莫妮卡
16年5月5日在13:04

@cmaster:是的,但是其他人如何编写宏(或其他语言功能)是我无法控制的。大括号的使用方法由我控制。我绝对不是说这是包含括号的铁定理由,但是我可以做些事情来保护自己免受他人的错误的影响,所以我做到了。

–韦恩
16年5月5日在13:31

@ gnasher729,谁在乎别人的错吗?如果您正在执行代码审查并遵循一些合理的代码标准,那么您的团队也很可能犯错。如果它到达了用户,他们将责怪该组织发布错误的软件。

–ideasman42
16年6月6日在15:52

谁提出宏是谁的错。

– Neme
17年3月3日在20:14

#6 楼

允许“鲍勃叔叔”发表意见,允许您发表意见。无需挑战他。

如果您想上诉权威,请选择克里斯·拉特纳(Chris Lattner)。在Swift中,if语句失去了括号,但始终带有花括号。没有讨论,这是语言的一部分。因此,如果“鲍勃叔叔”开始删除花括号,则代码将停止编译。

检查别人的代码并“摆脱无用的括号”是一个坏习惯。仅在需要检查代码以及不必要地产生冲突时才引起额外的工作。也许“鲍勃叔叔”是如此优秀的程序员,以至于他不需要代码审查?我浪费了一个星期的时间,因为一位顶级程序员在没有代码审查的情况下将“ if(p!= NULL)”更改为“ if(!p)”,这被隐藏在最糟糕的地方。

这主要是一场无害的风格辩论。花括号的优点是您可以插入另一行代码而无需添加花括号。就像日志记录语句或注释(如果后面紧跟注释,则太糟糕了)。在同一行上的语句似乎具有实际的缺点,即许多调试器都存在问题。但是随便你怎么做。

评论


我猜这是关于“具有挑战性的”,因为UB(原文如此!)将他的观点陈述为事实-至少当我听他的话时,我的观点如此。

–user88637
16年5月5日在12:07

说“做任何你喜欢的事”实际上是同意鲍勃叔叔的观点,因为那是他认为“没关系”的意思。在许多语言中,这不是问题。我以前曾在所有问题中都想到过,应该始终使用大括号,而没有人挑战这一问题。现在是Bob叔叔在挑战它,我想知道他是否正在摆脱它,是因为他以如此高度重构的微小方法工作,还是因为他吃饱了。由于@PaulDraper在回答中提到的各种错误,我没有想到它是无害的。

–candied_orange
16年5月5日在13:11

经常:语法噪音会分散注意力,右括号的多余“空白”行在段落中添加了视觉分隔符,其中这些行不应分开,进一步阻碍了可读性。在您提到的简洁小块中,这一点尤其重要。

–JDługosz
16年6月6日在14:42

#7 楼

我不删除括号的原因是:


减少决策疲劳。如果您始终使用花括号,则不必决定是否需要花括号。
减少开发负担:即使您最终尝试将所有多行逻辑提取到方法中,也必须将无花括号转换为如果要增加逻辑,这会拖累开发工作。因此,需要删除花括号,并在需要更多代码时再次添加花括号。小而烦人。


#8 楼

一位年轻的同事曾说过,我们认为多余和多余的牙套实际上对他有帮助。我不记得确切的原因,但是如果它能让他更快地编写更好的代码,那仅仅是保留它们的原因。
最后,我们达成了一个妥协,即将它们放在一行上不会令这样简短的前提令我难以理解/分散我的注意力。例如,

if (!param) { throw invalid_argument (blahblah); }
if (n < 2) { return 0; }
// real code for function starts here...


我还指出,这些介绍性前提条件通常是身体的控制流语句(例如return),因此担心添加第二个语句意味着在相同条件下忘记写括号是没有意义的。在这种情况下的第二条语句无论如何都是死代码,没有任何意义。

我怀疑阅读问题的流利性是由于人的大脑解析器被连接成将大括号作为条件条件的一部分而引起的声明。那不是C ++语法的准确表示,但是可能是首先学习某些其他语言的副作用(在这种情况下)。

评论


鲍勃叔叔可能认为没有它们,他可以更快地编写更好的代码。

–djechlin
16年6月6日,0:27

我和其他人也能熟练使用C ++。

–JDługosz
16年6月6日在4:15

“如果它允许他更快地编写更好的代码,那就是保留它们的唯一原因。” ++我有一个小儿子也这么说,因此,我们保留了它们。

–RubberDuck
16年6月6日在11:36

...因此,这就是独自按照鲍伯叔叔想要的方式这样做的原因。

–djechlin
16年6月6日在16:05

#9 楼

刚刚看完该视频,我注意到消除花括号可以使您更轻松地了解仍需要重构代码的位置。如果需要大括号,您的代码可能会更简洁一些。

已经说过,当您在团队中时,消除大括号可能有一天会导致意外行为。我说保留它们,当事情出错时,您会为自己省下很多头痛。

评论


在先前的10个答案中,这似乎并没有提供任何实质性的解释。

– gna
16-10-5在10:01

#10 楼


教我不要执行此操作,因为它可以通过添加另一个缩进的行(如果不是由if
控制的话)来引入一个
错误,这很容易。


如果您的代码已经过良好的单元测试,则这种“ bug”会在脸上爆炸。

我遵循的做法是避免使用任何无用和丑陋的括号来清理代码。

评论


当然,反之亦然:如果您的代码没有错误,则不需要任何单元测试。现实情况是,我们生活在一个不完善的世界中,在这个世界中有时会出现代码错误,有时还会出现测试错误。 (以我的经验,后者实际上更常见。)因此尽管测试肯定会降低错误的风险,但这并不是寻找其他方法来再次提高风险的借口。

–ruakh
16年6月6日在4:59



补充一下ruakh的评论:无法100%对代码进行单元测试。

–BЈовић
16年6月6日在7:46

请来看我的代码;)在练习TDD时,总是可以非常迅速地显示这种错误。

– Mik378
16年6月6日在8:01



@ Mik378事实并非如此。关键是,如果您使用了括号,您甚至不必担心。

– GoatInTheMachine
18-10-8在12:24