Skip to content

HugeGraph Code Style Guide

V_Galaxy edited this page Jul 12, 2024 · 10 revisions

Note: 加入 Apache (ASF) 后, 为了尽量降低用户提交代码的门槛以及减少不必要的风格检查 review, 注意换行/对齐时应尽量使用 IDE 推荐/自动的格式化, 减少以前大量的"手动"格式化, 因为这会给新用户带来很大的格式化困扰 (由于存在手动格式化与自动格式化产生冲突)

尤其要避免/减少无法定义规则的对齐, 我们的目标是尽可能让 95%+ 的代码可以自动被格式化, 极少数历史遗留代码需要思考/手动换行, 但仍不应该使用特殊人工 format 的规则, 后文中以此为基准再进行理解 (不确定度可以联系 PMC 同学)

1. 行长度

建议单行 ≤100, IDEA 强烈建议导入项目根下的 .editorconfig 配置文件进行格式化

Eclipse、Idea 等 IDE 均可针对项目进行设置,下文所述的换行对齐规则一般 IDE 也可设置。

好处:

  • 便于阅读
  • 命令行及 Github 可一行容纳下
  • 符合流行社区习惯

2. 对齐与缩进

对于字符串和函数参数,对齐优先级高于缩进。也就是说换行时,如果能与上一行同类代码对齐则尽量对齐。(不强制, 请勿使用人工且无法被 IDE 自动 format 的对齐规则)

注意: 下面的所有 case 都是当前行 > 100 字符的时候才考虑执行, 如果没有超过则优先一行, 历史 80 字符换行的, 看到可以合并到一行 (也可以 ignore)

2.1 多条件 if 语句换行对齐

这里为了美观忽略内部规范:多条件并列时,建议逻辑运算符写在行尾,下行条件与上行括号对齐。如:

if (query.condition(HugeKeys.SOURCE_VERTEX) != null &&
    query.condition(HugeKeys.DIRECTION) != null &&
    !keys.isEmpty() && query.matchUserpropKeys(keys)) {

2.2 长函数参数换行对齐

建议括号内的逗号处换行。如:

注:不同浏览器上对齐效果可能不一致,两个 Set 首字母是竖直对齐的。

protected void prepareAdditions(Set<HugeVertex> updatedVertexes,
                                Set<HugeEdge> updatedEdges) {

另外 throws 换行对齐是与函数参数保持一致。

2.3 函数调用换行参数对齐

建议括号内的逗号处换行,如:

instance.longFunction(param1,
                      param2);
instance.longLongFunction(
         param1,
         param2);

2.4 长字符串换行对齐

建议在明显分隔符出换行,如逗号后,且空格保留在上行。如果能在第二行容纳得下所有内容,则第一行空着以保留完整字符串。如:

throw new BackendException(
          "Not support querying by multi edge-labels");

2.5 三元表达式换行对齐

保持与条件判断语句风格一致,符号写在行末,两个分支都另起一行

Number n1 = (first instanceof Number) ?
            (Number) first :
            toBig.apply(first);

3. 空行

为了直观区分各个代码块,如下情况需要添加空行:

  • package 下一行
  • class、enum 定义的前一行和后一行
  • 成员变量定义完成与方法定义开始之间加空行
  • 方法定义、内部类定义、静态代码块等,它们之间加空行
  • 文件末尾保持一行空行
  • 方法内部逻辑相对独立的代码块使用空行分隔

4. 变量、方法命名

  • 变量、方法命名:驼峰命名,保持与 Google 规范一致即可。
  • 方法前缀:方法名不需要 get、set、is 前缀,以保持方法名称的简洁。

5. 逻辑 if-return 问题

关于 if-return-else-return 语句的清理咱们遵循的原则是:简短的错误处理代码放在方法体前部,if 后直接 return,后面不接 else;如果是并列的业务逻辑代码,可使用 if-return else-if-return else-return 以使得逻辑更加清晰;关于是否判断为并列逻辑,如果没有明显的边界,那么倾向使用原则 1。

  1. https://softwareengineering.stackexchange.com/questions/157407/best-practice-on-if-return

  2. https://blog.mozilla.org/nnethercote/2009/08/31/no-else-after-return-considered-harmful/

6. 注释风格

  • 注释写在代码上面,不要写在代码右边或下面 (不使用行尾注释)

  • 一行注释用 //,多行注释用 /**/

  • 类变量/方法的注释都使用 java-doc 的写法,即:

    /**
     * xxx
     */
  • 方法内的注释使用首行单星号的多行注释,即:
    /*
     * xxx
     * xx
     */

7. 错误检查

错误检查建议使用:E.checkArgument()E.checkState() 等工具函数代替 if + throw 的方式。

E.checkArgument(!primaryKeyIds.contains(propKey.id()),
                "Can't remove primary key '%s'", propKey);
 
E.checkState(prop.element() != null,
             "No owner for removing property '%s'", prop.key());

事实上 org.apache.hugegraph.util.E 直接调用 com.google.common.base.Preconditions,错误检查需频繁使用,使用 "E" 代替 "Preconditions" 的原因是为了减少代码的长度。

8. 异常处理

异常的处理一般在最上层调用处进行,或者外部 API 调用处进行。

如果进行异常转换时,建议通过日志打印原始异常的堆栈信息,或者将原始异常作为新异常的 cause 以记录异常栈。

try {
    return new GraphTransaction(this, this.loadGraphStore());
} catch (BackendException e) {
    String message = "Failed to open graph transaction";
    LOG.error("{}", message, e);
    throw new HugeException(message);
}

不允许存在吞异常的行为,吞异常会导致上层逻辑混乱:

try {
    return new GraphTransaction(this, this.loadGraphStore());
} catch (BackendException e) {
    e.printStackTrace();
}

如果明确的可以忽略异常,请使用 "ignored" 进行说明 try {...} catch (XxException ignored) {}

9. 日志打印

日志实例统一使用 LOG 命名,通过 Log.logger() 获取:

private static final Logger LOG = Log.logger(HugeGraph.class);

日志打印需要 "{}" 作为填充,而不是使用字符串拼接。

LOG.info("Opening backend store '{}' for graph '{}'",
         backend, this.name);

事实上 org.apache.hugegraph.util.Log 直接调用 org.slf4j.LoggerFactory,日志需频繁使用,使用 "Log" 代替 "LoggerFactory" 的原因依然是为了减少代码的长度。

10. 字符串拼接

建议优先使用 String.format() 进行字符串拼接,以增强代码可读性。

String message = String.format("Graph '%s' start backup!", this.graph);

不允许在打印日志或错误检查时拼接字符串,以避免无谓的浪费。

LOG.debug("Do query: " + query);
E.checkArgument(!query.empty(), "The query is empty: " + query);

11. 其它

其它未提及方面请以 Google Java Style Guide 为准。

附录

ASF 标准头 (file header), 新的代码/配置文件都应该采用这个排版 (注释方式自行调整), 有些旧排版的换行对齐不太合理, 应该予以统一替换

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */