4

我对我的代码的美学有一种特别愚蠢的不安全感......坦率地说,我对空格的使用很尴尬。我的代码看起来像极客跳舞;不是很吓人,但很尴尬,以至于你盯着看会感觉很糟糕,但又无法移开视线。

我只是不确定何时应该留下空白行或使用行尾注释而不是上面的行注释。我更喜欢在我的代码上方发表评论,但有时为了三个字的评论打断流程似乎很奇怪。有时在一段代码之前和之后加入一个空行就像在一段平滑的代码中放一个减速带。例如,在一个嵌套循环中,在中间分隔三行或四行代码块几乎抵消了缩进的视觉效果(我注意到 K&R 括号比 Allman/BSD/GNU 样式更不容易出现这个问题)。

我个人的偏好是密集的代码,除了函数/方法/注释块之间,几乎没有“减速带”。对于棘手的代码部分,我喜欢留下一个大的注释块,告诉你我将要做什么以及为什么,然后在该代码部分中添加一些“标记”注释。不幸的是,我发现其他一些人通常喜欢大方的垂直空白。一方面,我可以拥有更高的信息密度,而其他人认为流动性不是很好,另一方面,我可以以更低的信噪比为代价拥有更好的流动代码库。

我知道这是一件微不足道、愚蠢的事情,但这是我真正想要努力的事情,因为我提高了我的其他技能。

有人愿意提供一些提示吗?你认为什么是流畅的代码,在哪里使用垂直空白是合适的?对两三个字评论的行尾评论有什么想法吗?

谢谢!

PS 这是我一直在研究的代码库中的一种方法。不是我最好的,但不是我迄今为止最差的。

/**  
 * TODO Clean this up a bit.  Nothing glaringly wrong, just a little messy.  
 * Packs all of the Options, correctly ordered, in a CommandThread for executing.  
 */  
public CommandThread[] generateCommands() throws Exception
 {  
  OptionConstants[] notRegular = {OptionConstants.bucket, OptionConstants.fileLocation, OptionConstants.test, OptionConstants.executable, OptionConstants.mountLocation};  
  ArrayList<Option> nonRegularOptions = new ArrayList<Option>();  
  CommandLine cLine = new CommandLine(getValue(OptionConstants.executable));  

  for (OptionConstants constant : notRegular)  
   nonRegularOptions.add(getOption(constant));  

  // --test must be first  
  cLine.addOption(getOption(OptionConstants.test));  

  // and the regular options...  
  Option option;  
  for (OptionBox optionBox : optionBoxes.values())  
   {  
    option = optionBox.getOption();  
    if (!nonRegularOptions.contains(option))  
     cLine.addOption(option);  
   }  

  // bucket and fileLocation must be last  
  cLine.addOption(getOption(OptionConstants.bucket));  
  cLine.addOption(getOption(OptionConstants.fileLocation));  

  // Create, setup and deploy the CommandThread  
  GUIInteractiveCommand command = new GUIInteractiveCommand(cLine, console);  
  command.addComponentsToEnable(enableOnConnect);  
  command.addComponentsToDisable(disableOnConnect);  
  if (!getValue(OptionConstants.mountLocation).equals(""))  
   command.addComponentToEnable(mountButton);  

  // Piggy-back a Thread to start a StatReader if the call succeeds.  
  class PiggyBack extends Command  
   {  
    Configuration config = new Configuration("piggyBack");  
    OptionConstants fileLocation  = OptionConstants.fileLocation;  
    OptionConstants statsFilename = OptionConstants.statsFilename;  
    OptionConstants mountLocation = OptionConstants.mountLocation;  

    PiggyBack()  
     {  
      config.put(OptionConstants.fileLocation, getOption(fileLocation));  
      config.put(OptionConstants.statsFilename, getOption(statsFilename));  
     }  

  @Override  
  public void doPostRunWork()  
   {  
    if (retVal == 0)  
     {  
// TODO move this to the s3fronterSet or mounts or something.  Take advantage of PiggyBack's scope.  
      connected = true;  
      statReader = new StatReader(eventHandler, config);  
      if (getValue(mountLocation).equals(""))  
       {  
        OptionBox optBox = getOptionBox(mountLocation);  
        optBox.getOption().setRequired(true);  
        optBox.requestFocusInWindow();  
       }  

      // UGLY HACK... Send a 'ps aux' to grab the parent PID.  
      setNextLink(new PSCommand(getValue(fileLocation), null));  
      fireNextLink();  
     }  
   }  
 }  

PiggyBack piggyBack = new PiggyBack();  
piggyBack.setConsole(console);  
command.setNextLink(piggyBack);  
return new CommandThread[]{command};  
}  
4

13 回答 13

11

没关系。

1) 发展一种你自己的风格。无论你觉得最容易和最舒服的是什么,都去做。尽量保持一致性,但不要成为一致性的奴隶。拍摄约 90%。

2) 当您修改其他开发人员的代码,或处理小组项目时,请使用代码库中存在的或样式指南中规定的样式约定。不要抱怨它。如果您能够定义风格,请提出您的偏好,但愿意妥协。

如果你同时遵循这两个,你就会万事俱备。把它想象成以两种不同的方式说同一种语言。例如:在朋友面前说话的方式与在祖父面前说话的方式不同。

于 2009-06-18T05:32:10.663 回答
5

编写漂亮的代码并不是一件小事。当我写了一些我真正引以为豪的东西时,我通常可以退后一步,查看整个方法或类,一眼就知道它做了什么——甚至几个月后。美学在其中起到了一定的作用,尽管没有好的设计那么大。另外,意识到你不能总是写漂亮的代码,(无类型的 ADO.NET 任何人?)但是当你可以的时候,请这样做。

不幸的是,至少在这个更高的级别上,我不确定您是否可以遵守任何硬性规则来始终生成美观的代码。我可以提供的一条建议是简单地阅读代码。很多。在许多不同的框架和语言中。

于 2009-06-18T05:34:02.860 回答
4

我喜欢用空格分解代码的逻辑“短语”。这有助于其他人轻松地可视化方法中的逻辑 - 或者在我返回查看旧代码时提醒我。例如,我更喜欢

reader.MoveToContent();
if( reader.Name != "Limit" )
    return false;

string type = reader.GetAttribute( "type" );
if( type == null )
    throw new SecureLicenseException( "E_MissingXmlAttribute" );

if( String.Compare( type, GetLimitName(), false ) != 0 )
    throw new SecureLicenseException( "E_LimitValueMismatch", type, "type" );

代替

reader.MoveToContent();
if( reader.Name != "Limit" )
    return false;
string type = reader.GetAttribute( "type" );
if( type == null )
    throw new SecureLicenseException( "E_MissingXmlAttribute" );
if( String.Compare( type, GetLimitName(), false ) != 0 )
    throw new SecureLicenseException( "E_LimitValueMismatch", type, "type" );

使用大括号几乎可以实现相同的中断,但我发现这实际上增加了视觉噪音并减少了可以同时在视觉上消耗的代码量。

代码行上的评论

至于行尾的评论 - 几乎从来没有。这还不错,只是在扫描代码时很容易错过。它们使代码变得混乱,使代码更难阅读。我们的大脑已经被连接到一行一行地摸索。当注释位于行尾时,我们必须将该行拆分为两个具体概念 - 代码和注释。我说如果它足够重要,可以评论,把它放在代码后面的那一行。

话虽如此,我确实发现关于特定值含义的一两行提示注释有时是可以的。

于 2009-06-18T05:50:44.563 回答
3

我发现很少有空格的代码很难阅读和导航,因为我需要实际阅读代码才能找到其中的逻辑结构。巧妙地使用空格来分隔函数中的逻辑部分可以增加理解代码的便利性,不仅对作者而且对其他人也是如此。

请记住,如果您在一个代码可能由他人维护的环境中工作,他们将花费大部分时间查看不是您编写的代码。如果您的风格与他们习惯看到的截然不同,那么您流畅的代码可能会成为他们的减速带

于 2009-06-18T05:40:19.510 回答
1

我尽量减少空白。我将主要注释块放在代码块上方,并将附加的行尾注释放在其他开发人员可能不明显的东西上。我想你已经这样做了

于 2009-06-18T05:34:33.123 回答
1

对于大多数开发人员来说,我喜欢的风格可能是厌恶的,但我会偶尔添加空行来分隔看起来合适的代码“段落”。它对我有用,在代码审查期间没有人抱怨(但是!),但我可以想象它对其他人来说可能是任意的。如果其他人不喜欢它,我可能会停止。

于 2009-06-18T05:43:07.027 回答
1

要记住的最重要的事情是,当您加入现有代码库时(在您的职业生涯中几乎总是这样),您需要遵守项目规定的代码样式指南。

许多开发人员在重新开始一个项目时,会选择使用基于 Linux 内核编码样式文档的样式。该文档的最新版本可以在http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=Documentation/CodingStyle;h=8bb37237ebd25b19759cc47874c63155406ea28f查看;hb=头

同样,许多维护者坚持在向版本控制提交更改之前使用 Checkpatch。您可以在我在上面链接到的同一棵树的 scripts/checkpatch.pl 中看到 Linux 内核附带的最新版本(我会链接到它,但我是新手,每个答案只能发布一个超链接)。

虽然 Checkpatch 与您关于空格使用的问题没有特别相关,但它肯定会帮助您消除尾随空格、制表符前的空格等。

于 2009-06-18T13:49:25.580 回答
1

我使用的空格数量与您完全相同:) 方法之前,注释块之前的空格。在 C、C++ 中,括号还提供了一些“伪空白”,因为在某些行上只有一个左大括号/右大括号,因此这也有助于打破代码密度。

于 2009-06-18T13:53:33.590 回答
1

代码完成,史蒂夫·麦康奈尔(Steve McConnell)(在通常的位置有售)是我关于这类事情的圣经。它有一整章关于布局和风格的内容非常好。整本书充满了有用和实用的建议。

于 2009-06-18T13:58:48.120 回答
1

你的代码很好,只要做你(和你可能与之合作的其他人)觉得舒服的事情。

我认为一些(没有经验的)程序员关于空格的唯一错误是他们可能害怕使用它,在这种情况下这是不正确的。

然而,我确实注意到您在示例代码中没有使用超过一个连续的空白行,在某些情况下,您应该使用它。

于 2009-07-02T20:02:56.327 回答
1

以下是我将如何重构该方法。事情肯定还可以改进,我还没有重构 PiggyBack 类(我只是把它移到了上层)。

通过使用组合方法模式,当代码被划分为每个做一件事并在单一抽象级别上工作的方法时,代码变得更易于阅读。也需要更少的评论。回答“什么”问题的注释是代码气味(即代码应该被重构以更具可读性)。有用的注释回答了“为什么”这个问题,即使这样,最好还是改进代码,这样原因就很明显了(有时可以通过一个没有不明显代码的测试来完成)。

public CommandThread[] buildCommandsForExecution() {
    CommandLine cLine = buildCommandLine();
    CommandThread command = buildCommandThread(cLine);
    initPiggyBack(command);
    return new CommandThread[]{command};
}

private CommandLine buildCommandLine() {
    CommandLine cLine = new CommandLine(getValue(OptionConstants.EXECUTABLE));
    // "--test" must be first, and bucket and file location must be last,
    // because [TODO: enter the reason]
    cLine.addOption(getOption(OptionConstants.TEST));
    for (Option regularOption : getRegularOptions()) {
        cLine.addOption(regularOption);
    }
    cLine.addOption(getOption(OptionConstants.BUCKET));
    cLine.addOption(getOption(OptionConstants.FILE_LOCATION));
    return cLine;
}

private List<Option> getRegularOptions() {
    List<Option> options = getAllOptions();
    options.removeAll(getNonRegularOptions());
    return options;
}

private List<Option> getAllOptions() {
    List<Option> options = new ArrayList<Option>();
    for (OptionBox optionBox : optionBoxes.values()) {
        options.add(optionBox.getOption());
    }
    return options;
}

private List<Option> getNonRegularOptions() {
    OptionConstants[] nonRegular = {
            OptionConstants.BUCKET,
            OptionConstants.FILE_LOCATION,
            OptionConstants.TEST,
            OptionConstants.EXECUTABLE,
            OptionConstants.MOUNT_LOCATION
    };
    List<Option> options = new ArrayList<Option>();
    for (OptionConstants c : nonRegular) {
        options.add(getOption(c));
    }
    return options;
}

private CommandThread buildCommandThread(CommandLine cLine) {
    GUIInteractiveCommand command = new GUIInteractiveCommand(cLine, console);
    command.addComponentsToEnable(enableOnConnect);
    command.addComponentsToDisable(disableOnConnect);
    if (isMountLocationSet()) {
        command.addComponentToEnable(mountButton);
    }
    return command;
}

private boolean isMountLocationSet() {
    String mountLocation = getValue(OptionConstants.MOUNT_LOCATION);
    return !mountLocation.equals("");
}

private void initPiggyBack(CommandThread command) {
    PiggyBack piggyBack = new PiggyBack();
    piggyBack.setConsole(console);
    command.setNextLink(piggyBack);
}
于 2009-07-02T21:08:15.837 回答
1

对于 C#,我说 "if" 只是一个词,而 "if(" 是代码 - "if"、"for"、"try" 等后面的空格根本不利于可读性,所以我认为它更好没有空间。

另外:Visual Studio> 工具> 选项> 文本编辑器> 所有语言> 选项卡> 保持选项卡!

如果你是一个坚持使用制表符所在的空格的软件开发人员,我会坚持认为你是个懒汉——但无论如何——最后,它都是编译好的。另一方面,如果你是一个 Web 开发人员,在你的 HTML/CSS/JavaScript 上都有一堆连续的空格和其他多余的空格,那么你要么对客户端代码一无所知,要么就是不给废话。客户端代码未编译(也未使用 IIS 默认设置压缩) - 客户端脚本中无意义的空格就像在服务器端代码中添加无意义的 Thread.Sleep() 调用。

于 2009-11-10T16:42:08.147 回答
0

我喜欢最大化可以在窗口中看到的代码量,所以我只在函数之间使用一个空行,很少在函数内使用。希望你的函数不会太长。看你的例子,我不喜欢一个空括号的空行,但我会有一个结束。压痕和着色应该足以显示结构。

于 2009-06-18T12:57:39.943 回答