65.9K
CodeProject 正在变化。 阅读更多。
Home

如果名称中包含空格会怎样?

2016 年 1 月 4 日

CPOL

4分钟阅读

viewsIcon

23151

downloadIcon

47

常量、变量、方法、类和命名空间的名称中不允许使用空格是否存在技术原因,还是符合某种约定?

引言

常量、变量、方法、类和命名空间的名称中不允许使用空格是否存在技术原因,还是符合某种约定?这个问题也可以问及不能放在名称开头的数字。

遵循命名约定的主要原因是减少阅读和理解源代码所需的精力。开发者可以专注于比争论语法和命名标准更重要的问题。因此,有一个严格的规则,即名称通常区分大小写。名称可以是任何合法的标识符——一个无限长度的 Unicode 字母和数字序列,以字母或下划线字符开头。名称中不允许有空格。

最后一条规则不仅用于解决代码理解方面的困难。编译器需要找出单词的含义。它使用“状态机”方法,需要区分关键词。但是,像 SQL 或 Maple 这样的语言允许名称包含空格。它们用特殊的引号字符括在两边:SQL 中的“[”和“]”,Maple 中的“`”(U+0060,重音符)。在 C# 中,方括号用作集合项目的索引器。但是重音符可以成功地用作封闭符号。

代码示例

让我们尝试用 C# 编写代码,使用包含空格的相当长的名称,看看会发生什么。

[`Custom Serialization Attribute`]
public class `Config Data File Repository` : `Configuration Repository Interface` {
  public enum `Data Visualization Format` { `2D`, `3D` }
  public `Data Visualization Format` `Visualization Format` { get; set; } 
    = `Data Visualization Format`.`2D`;
  ... 
  private readonly string `repository file name`;
  public `Config Data File Repository`(string `repository file name`) {
    this.`repository file name` = `repository file name`;
  }
  public `Configuration Data` Load() {
    using(var fs = new `File Stream`(`repository file name`, `File Mode`.Open)) {
    return `Load From Stream`(fs);
    }
  }
  public void Save(`Configuration Data` `current configuration`) {
    using (var fs = new `File Stream`(
      this.`repository file name`, `File Mode`.`Create New`)) {
      `Save To Stream`(`current configuration`, fs);
    }
  }
  ...
  public const string `DEFAULT REPOSITORY FILE` =
    "Default Config Data File Repository.config";
  
  public static `Config Data File Repository` `Create Config Data File Repository`(
    string `file Name` = `Config Data File Repository`.`DEFAULT REPOSITORY FILE`) {
    return new `Config Data File Repository`(`file Name`);
  }
}

此外,让我们用 Java 编写代码,这与现实世界的问题非常接近。

@Entity
@Table(name = "[PERSONAL CONFIGURATION SECTIONS]")
@EntityListeners( { HierarchyListener.class } )
@NamedQueries({
  @NamedQuery(
    name = "`Personal Configuration Section`.`find All`",
    query = "SELECT cs FROM [PERSONAL CONFIGURATION SECTIONS] cs"),
  @NamedQuery(
    name = "`Personal Configuration Section`.`find By Configuration Section Id`",
    query = "SELECT cs FROM [CONFIGURATION SECTIONS] cs "
      + "WHERE cs.[configuration Section Id] = :`configuration Section Id`")})
public class `Personal Configuration Section`
  implements Serializable, `Hierarchy Element Interface` {
  private static final long `serial Version UID` = 1L;
  @Id @NotNull
  @Basic(optional = false)
  @`Generated Value`(
    strategy = `Generation Type`.SEQUENCE,
    generator = "global_id_gen")
  @`Sequence Generator`(name = "global_id_gen", `sequence Name` = "GLOBAL_ID_GEN")
  @Column(name = "[CONFIGURATION SECTION ID]")
  private Integer `configuration Section Id`;
  public Integer `get Configuration Section Id`()
    { return `configuration Section Id`;  }
  public void `set Configuration Section Id`(Integer `configuration Section Id`)
    { this.`configuration Section Id` = `configuration Section Id`; }
  
  @Column(name = "[FULL NAME]")
  private String `full Name`;
  public String `get Full Name`()
    { return `full Name`;  }
  public void `set Full Name`(String `full Name`)
    { this.`full Name` = `full Name`; }
  
  @Column(name = "[EXPERIENCE, SKILLS AND ARTIFACTS]")
  private `Collection Interface` `experience, skills and artifacts`;
  public `Collection Interface` `get Experience, skills and artifacts`()
    { return `experience, skills and artifacts`; }
  public void `set Experience, skills and artifacts`(
    `Collection Interface` `experience, skills and artifacts`)
    { this.`experience, skills and artifacts` = `experience, skills and artifacts`; }
  
  @`Many To One`(fetch = `Fetch Type`.LAZY)
  @`Join Columns`({
    @`Join Column`(
      name="[PARENT ID]",
      `referenced Column Name`="parent Configuration Section Id"),
    @`Join Column`(
      name="[PARENT VERSION]",
      `referenced Column Name`="parent Configuration Section Version")
    })
  private `Personal Configuration Section` parent;
  public `Personal Configuration Section` `get Parent`()
    { return parent; }
  public void `set Parent`(`Personal Configuration Section` parent)
    { this.parent = parent; }
  
  @`One To Many`(fetch=`Fetch Type`.LAZY, `mapped By`="parent")
  private Set<`Personal Configuration Section`> children;
  public Set<`Personal Configuration Section`> `get Children`() { return children; }
  
  public `Personal Configuration Section`() {}
  
  public `Personal Configuration Section`(
    Integer `personal Configuration Section Id`) {
    this.`personal Configuration Section Id` = `personal Configuration Section Id`;}
  
  ...
}

毫无疑问,可以使编译器解析此代码。但是,代码变得更难以阅读。令我惊讶的是,代码的可读性只是略有下降,尤其是对于 Java 而言!首先是已经存在的名称过长的问题。有大量文章(“编程搭档认为提供良好描述的非常长的描述性名称”或“变量和方法命名的最佳实践”)提供了一些有用的解决方案。这个问题自面向对象语言诞生以来就一直存在。根据我的观点,在思考代码时,从可能使用它的其他开发人员的角度来看它是有用的。这将不仅有助于选择名称,而且有助于使您的代码更具可读性,而无需编写文档和不必要的注释。如果名称太长,那么这部分代码可能具有多个职责,应该单独编写。换句话说,代码应该被重构。

此外,还有一个问题,即如何提高可读性以及在哪里使用该功能。主要的应用程序领域可以定义为单元测试类和包含由 string 资源或配置设置部分包装类实现的国际化 string 属性的类。由于 IDE 的功能,可读性可以得到提高。例如,带有空格的名称可以用细虚线边框突出显示。这种方法将允许开发人员直观、快速地识别此类名称,并将限制对带有空格的名称的广泛、草率的使用。

让我们尝试用 C# 编写一个简单的单元测试。

此外,上面给出的单元测试用 Java 重新编写。

C# 和 Java 中的单元测试代码具有非常好的可读性。它不需要其他属性就能在测试报告窗口中正确显示。但是,指定测试类别的属性可能会很有用。

在代码中经常使用这种长名称不是最好的方法。但是,如果需要引用它们,则可以使用自然语言中提出的方法来减少此类名称。名称中的一些不重要部分可以用省略号字符代替。

结论

我非常感谢读者表达他们的观点并回答以下问题。重新考虑名称带空格的情况是否已经到来?并且在哪些领域编写此类名称非常重要?也许是时候重新思考命名约定了。

关注点

很高兴找到并学习好的软件工程原则,源代码中的好方法和现代框架使我们能够编写真正灵活的代码。

历史

  • 2016 年 4 月 1 日 - 第一个版本
© . All rights reserved.