【问题标题】:Using { } to segment large blocks of code to improve code-readability - Good practice?使用 { } 分割大块代码以提高代码可读性 - 好的做法?
【发布时间】:2010-10-25 17:28:19
【问题描述】:

我正在考虑使用匿名 { } 代码块来在逻辑上区分同一方法调用中的“代码块”,这(理论上)应该提高代码的可读性。 p>

我想知道以下 2 个代码段中哪一个更适合您?

另外,两个代码段是否编译为相同的字节码?换句话说,使用 { } 会以任何方式损害代码的性能吗?

选项 1:没有 { } 标识的代码块

 public static String serviceMatch(HttpServletRequest servletRequest, RequestTypeEnum requestTypeEnum, ...censorsed..., RequestStatistics requestStatistics) {
  Request request;

  // We get the parser that fits the ...censorsed..., effectively transforming the HTTPReqeuest to application local "Request*" object
  RequestParser parser = RequestParserFactory.getParser(...censorsed...);

  // Populate basic parameters, the "heavy" data will be lazy loaded
  request = parser.parse(servletRequest);

  // Instead of polluting the parsers let's put it here... (unless we identify meaningful justifications for the other alternative of changing RequestParser.parse() interface.
  request.requestType = requestTypeEnum;

  // Store the request statistics object on the request, so that we have access to it from all over the code
  request.requestStatistics = requestStatistics;



  // Update timestamp when request was parsed
  request.requestStatistics._1_end_parseRequest = System.currentTimeMillis();


  /*
   * ...censorsed...
   */
  MatchResult matchResult = Matcher.findMatch(...censorsed...);

  /*
   * ...censorsed...
   */
  String reply = ReplyFormatFactory.getFormatter(...censorsed...

  // Update timestamp when reply finished construction
  request.requestStatistics._6_end_formatReply = System.currentTimeMillis();

  return reply;
 }

选项 2:带有 { } 标识的代码块

 public static String serviceMatch(HttpServletRequest servletRequest, RequestTypeEnum requestTypeEnum, ...censorsed..., RequestStatistics requestStatistics) {
  Request request;

  /*
   * Request parsing block
   */
  {
   // We get the parser that fits the ...censorsed..., effectively transforming the HTTPReqeuest to application local "Request*" object
   RequestParser parser = RequestParserFactory.getParser(...censorsed...);

   // Populate basic parameters, the "heavy" data will be lazy loaded
   request = parser.parse(servletRequest);

   // Instead of polluting the parsers let's put it here... (unless we identify meaningful justifications for the other alternative of changing RequestParser.parse() interface.
   request.requestType = requestTypeEnum;

       // Store the request statistics object on the request, so that we have access to it from all over the code
   request.requestStatistics = requestStatistics;
  }



  // Update timestamp when request was parsed
  request.requestStatistics._1_end_parseRequest = System.currentTimeMillis();


  /*
   * ...censorsed...
   */
  MatchResult matchResult = Matcher.findMatch(...censorsed...);

  /*
   * ...censorsed...
   */
  String reply = ReplyFormatFactory.getFormatter(...censorsed...

  // Update timestamp when reply finished construction
  request.requestStatistics._6_end_formatReply = System.currentTimeMillis();

  return reply;
 }

感谢您的评论,马克西姆。

【问题讨论】:

  • 两个选项都被命名为“没有 { } 标识的代码块”
  • 选项 2 应该是“带有 {} 缩进的代码块”,对吧?
  • 是的,修正了错字。感谢您的关注。
  • 这个应该移到CodeReview
  • @Nimrod:我是原始发帖人,我同意这条消息。 :) 认为搬家是个好主意。

标签: java performance coding-style code-readability


【解决方案1】:

如果您想在同一方法中添加额外的 { } 只是为了便于阅读,我的建议是考虑将您的方法重构为几个较小的方法。
这些较小的方法具有优势更容易自己理解,并且更可重用(如果它们“松散耦合”)。请参阅single responsibility principle

【讨论】:

  • @Maxim Veksler:对我来说听起来像是过早的优化。
  • 如果您为新的较小方法使用好名字,您也可以“评论”
  • @Maxim Veksler:虽然我不知道你的申请细节,但我认为你在这一点上是错误的。引用 Effective Java 的话,努力编写好的程序而不是快速的程序。 一个编写良好、松散耦合和可读的程序将使您在分析和识别出性能问题后更容易纠正这些问题问题。此外,您可能认为有助于提高性能的事情可能不会,甚至可能会阻止您以后在某些方面提高性能。
  • 我最近发现了 Martin Fowler 的书:重构:改进现有代码的设计。我希望它早一点。这是一本必读的书。通过不将代码拆分为方法(例如,及时编译 - en.wikipedia.org/wiki/Java_performance),编译器能够进行比您更好的优化。你需要在重构之前和之后进行分析,然后你现在得到什么,你失去了什么。
  • @Maxim:你有没有实际测试过这段代码的性能,发现拥有一个大方法比调用几个小方法更快?如果您没有对此进行测试并根据实际结果做出决策,那么您只是在猜测。
【解决方案2】:

如果您发现将括号放在代码的某些部分(如选项 2 中)会很方便,您应该将其移至自己的方法。这就是提高可读性的原因。

顺便说一句,我还认为您实际上不需要对代码的每一行都进行注释。例如,即使没有注释,时间戳更新也是不言自明的。

【讨论】:

  • 我怀疑 cmets 可能是企业需求,但是是的,它们有点多。
  • @Jonathan Grynspan:程序员不是官僚——如果代码可读并且具有相应数量的 cmets,那么没有人会解雇程序员。至少没有人头脑清晰:-)
  • @dark:是的,但许多官僚不理解 cmets 的目的,除了“让代码更好”。所以他们要求每一行都被评论。当人们看到每一行都带有注释的代码时,这通常是组织中规章制度的结果。 (当然,并非总是如此!而且每行都有 cmets 总比没有 cmets 要好。)
  • @Jonathan:我不会说每一行的 cmets 都比没有 cmets 好,特别是如果代码被明智地分解为命名良好的方法。即使不是,每条线的 cmets 也只是一个极端情况,其中两个极端都是坏的,而好的则在中间的某个地方。
  • @ColinD:哦,我同意——适度是最好的——但在任何相当复杂的程序中,都会有一些真正需要记录的函数或类,并且 cmets 每一行都会给你,而没有人让你陷入黑暗。
【解决方案3】:

我一般不会在没有语法原因的情况下添加大括号分隔的块,但如果变量只需要在有限的范围内,我宁愿创建一个嵌套的范围而不是在更大的中间定义变量一个(因为在后一种情况下,当变量超出“有用”范围时没有明确的指示)。

至于将这样的代码块提取到另一个方法中,我认为如果生成的方法(1)具有合理的参数批次,并且(2)可以给出描述其行为的名称,那么这是一个好主意和实际代码一样。如果使用该方法需要传递过多的参数,或者必须查看方法中的代码以了解其调用者在做什么,那么我认为最好使用匿名范围块。

【讨论】:

    【解决方案4】:

    我认为这有点主观,没有正确或错误的答案......我的意见是不要这样做。使用注释块分隔代码块并解释它们的不同之处为什么,但不要使用大括号。当我看到大括号时,我立即认为应该有一个领先的ifwhile,或者什么的......而且没有找到是有点奇怪。

    【讨论】:

      【解决方案5】:

      您可能应该改用单独的方法。你可以调用第一个块processRequest。任何阅读此代码的人都将能够看到使用了哪些参数、返回了哪些数据、它做了什么(即使没有 cmets)。区块不提供此类信息。

      字节码可能相同。

      【讨论】:

        【解决方案6】:

        有时更喜欢使用第二个选项。当提取单独的方法会导致多个返回参数混乱(即,将它们包装在人造对象中)时,就会发生这种情况。

        【讨论】:

          【解决方案7】:

          Lighttpd在配置文件中有一个注释块,用这种风格制作;

          #{{{ module name
          module.option = value;
          module.option = value;
          #}}} 
          

          因此您可以只评论而不是 {} 编写您的代码。

          在 Perl 中,{ }、sub { } 或 eval { } 中的任何内容都会被计算;但是,在某些子例程中保留大量 { } 块被认为足以将代码分成较小的部分;

          $html .= 评估 { $val = &getNextPiece();返回 $val; };

          所以这种做法是众所周知的。

          【讨论】:

            【解决方案8】:

            大括号通常用于对控制结构等的语句进行分组。我发现它们在用于其他任何事情时都很刺耳。

            如果我有一个过长的函数(无论出于何种原因)我不想拆分,我会用 cmets 将它分成块。

            【讨论】:

              【解决方案9】:

              大括号{ } 有其用途(在Java 7 中更是如此),我认为它们很少仅用于可读性。就个人而言,如果它们像在选项 2 中那样使用,我首先想到的是,“这是一个静态块吗?”。因此,我发现选项 1“更正常”且可读性强。

              如果您真的热衷于坚持使用一种方法而不是像这里许多人建议的那样重构这段代码,那么请改用 cmets 作为行分隔符。比如:

                  /* -------------------------------------------- */
                  /* describe in detail here why you don't want to put this in another method */
                  /* so other readers will know why! */
              
                 // We get the parser that fits the ...censorsed..., effectively transforming the HTTPReqeuest to application local "Request*" object
                 RequestParser parser = RequestParserFactory.getParser(...censorsed...);
              
                 // Populate basic parameters, the "heavy" data will be lazy loaded
                 request = parser.parse(servletRequest);
              
                 // Instead of polluting the parsers let's put it here... (unless we identify meaningful justifications for the other alternative of changing RequestParser.parse() interface.
                 request.requestType = requestTypeEnum;
              
                     // Store the request statistics object on the request, so that we have access to it from all over the code
                 request.requestStatistics = requestStatistics;
                }
                 /* -------- END of confusing block ------------- */
              

              恕我直言,cmets 可能是使代码可读的最佳选择。

              【讨论】:

              • 我们必须在问题的上下文中考虑这一点,即由于性能原因,重构不是一个选项。
              【解决方案10】:

              如果您使用 C# 进行开发,我建议您使用 #region ... #endregion 来代替以提高可读性。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2010-10-07
                • 1970-01-01
                • 2010-11-11
                • 2016-02-19
                • 1970-01-01
                • 1970-01-01
                • 2020-11-18
                相关资源
                最近更新 更多