浅谈:论代码规范

0、前言

本文包含对《阿里巴巴Java开发手册(终极版 1.3.0)》一、编程规约的部分讨论及本文作者(以下简称为作者)的个人见解,文章中并不会详细讨论各个规范,而是会挑选部分进行描述。由于作者对Java的应用通常在LWJGL及OpenGL,对Web开发不算太了解,文中可能会参杂部分其余规范讨论,亦或者与书中存在偏差。此外,本文中假设开发、运行环境皆为性能(如CPU、内存、存储空间)没有太大限制的场景。

0.1、为什么要规范代码

        一言以蔽之:代码,或现今的高级编程语言,是给人看的......

尽管此话并不严谨,但是在开发大体量程序或团队协作开发中,程序的性能往往并不是在开始编写的时候为首要考虑点,除非该程序目的就是为了优化,换句话说——程序开发初期不应过早/过度优化,而是应该考虑程序的延展性可维护性(注:当然,如果开发初期就出现了严重的性能问题还是应该尽快解决的)。而基于上述几点,一套代码规范就显得尤为重要了。

0.2、规范的代码可以做什么

规范的代码可以使得编写程序的人(如编写该程序的人或调用该部分程序的人)更好的理解一段程序的意图,从而更好地运用和维护该程序。不仅仅是团队开发,就算个人开发程序,也应该遵守一定的代码规范,这可以使得后续维护早期编写程式更顺利。而对于团队开发而言,遵循指定的规范编写程序可以降低团队合作过程中的阻力。

在个人开发的情况下,由于程序都是根据个人习惯的格式编写的,所以阅读起来不会有太大的阻力;但是在团队中,每位成员可能都有自己不同的习惯,使得每个人习惯其他人的编程习惯显然并不合理,因为个人习惯的编程格式并不一定是合理且科学的。由此,若团队中每个人按照同一个格式(规范)编写代码,对于团队中的每个人而言,编写的代码风格是近乎一致的,那在合作时由于代码产生的阻力也就会减少。

1、命名

命名是编程中非常基础且重要的一环,清晰且明确的命名可以使得代码自文档化,且可以降低维护成本、提升代码的可维护性和可扩展性。

1.1、基本准则

良好的命名规范能直观地反映变量、方法或类的用途及其功能,使得其他开发者(或自己)在阅读代码时能更快速地理解其意图。命名尽量使用完整的单词(组合)表达其含义,避免使用单个字符或无意义的组合。而且,命名不应当使用下划线或美元符号开始或结束命名,这是因为避免与系统底层或特定库中的特殊符号冲突,且提高代码可读性。例如:grams、volume、requestPath是可被接受的名称,而a、$volume、_path_是不可被接受的名称。

此外,也不应当使用拼音与英文混合的方式,也不应当直接将中文作为名称使用。最好避免使用拼音作为名称使用,以避免歧义,而一些国际通用的名称或官方译名可以直接当作英文使用。

1.2、命名风格

在编程中,使用特定的命名规范是一种广泛接受的最佳实践,它有助于代码的可读性、可维护性和一致性,且更易于区分不同类型的标识符:

 Java中,类担当了对象的蓝图或模板一功能,代表了一组具有共同属性和行为的对象的集合。使用UpperCamelCase(大驼峰)命名法可以使其在代码中更为突出,易于与其他标识符区分开。对于抽象类,使用Abstract或Base做前缀;异常类用Exception做后缀。

方法名、参数名、成员变量、局部变量则统一使用lowerCamelCase(小驼峰)命名法。这一类通常代表类的行为、数据、方法的输入和临时数据的标识符,使用小驼峰命名法在视觉上可保证足够的紧凑型,且同时保持足够的可读性。

常量(CONSTANCE)和枚举项(ENUM)的命名使用全部大写并使用下划线进行分隔,大写字母和下划线的组合方式使得常量名更加醒目,这样可以很容易将其与其他标识符进行区分。

包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。杜绝完全不规范的缩写,避免望文不知义。对于使用了设计模式的类,在命名时要体现具体模式。

2、代码格式

一段格式良好的代码可以使得整个项目看着更整洁、有序,可以减少阅读时的困惑,且有助于快速把握代码的结构和逻辑。良好的代码结构可使得未来的修改和扩展变得更加容易,并且由于其可读性较高的前提,有助于提前发现并修正潜在的错误、更好的进行测试。

2.1、代码块

对于大括号:若其内容为空,则直接写成 {} ,无需换行。如果是非空,则按照:左大括号前不换行后换行;右括号前换行,后有else的话不换行,表示终止的右括号需换行。

对于小括号:左小括号与右方相邻字符间不能添加空格,而右小括号与左方相邻字符间也不能添加空格。

if / for / while / switch / do 等保留字与括号之间都必须加空格。

缩进使用4个空格而不是tab字符。

public static void function(Predicate<String> predicate) { // 此处不换行
    if (predicate.test("value")) {
        // something...
    } else { // 此处也不换行
        // something else...
    }
} // 结束换行

2.2、换行

在阿里巴巴Java规范中,要求将一行代码字数限制在120字以内,超出的内容需要换行。第二行相较第一行需缩进4空格,自第三行其与第二行保持同样缩进。运算符、方法调用的标点符号需与下文一同进行换行。方法调用中的参数,如果需要换行,在逗号后进行,而括号前无需换行。在方法体内,执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。

对代码适当换行可以增加一定的可读性,特别是对于编写api接口或者库的开发者,合理的将接口函数的参数进行换行会有效提高阅读效率,如下:

// 该代码会使得阅读该接口的人较为难以理解
public interface IRenderer {
    void render(@NotNull ModelData modelData, @NotNull FrameBuffer frameBuffer, @NotNull ShaderInstance shaderInstance, @NotNull Matrix4f transformationMatrix, @NotNull ARGB32 color, @NotNull Camera camera);
}

// 相反,适当换行可以使得接口看着更为明了
public interface IRenderer {
    void render(
        @NotNull ModelData modelData, 
        @NotNull FrameBuffer frameBuffer, 
        @NotNull ShaderInstance shaderInstance, 
        @NotNull Matrix4f transformationMatrix, 
        @NotNull ARGB32 color, 
        @NotNull Camera camera
    );
}

其次,对于流处理,换行可以使得条例更加清晰:相较于反例,正例很明显在逻辑上会更清晰,且查看会更方便。

// 反例
public static List<Target> getTargetFromEntries() {
    return OBJECT_ENTRIES.stream().map(ObjectEntry::getMetaData).filter(MetaData::isEnabled).map(MetaData::getTarget).toList();
}

// 正例
public static List<Target> getTargetFromEntries() {
    return OBJECT_ENTRIES.stream()
        .map(ObjectEntry::getMetaData)
        .filter(MetaData::isEnabled)
        .map(MetaData::getTarget)
        .toList();
}

此外,对于一些特殊类型的函数或类构造,合理的换行可以更明确地表达该函数的用途(此处以joml库的Matrix4f为例):相比较反例,正例能更明确的告知阅读代码的人,该对象是一个4x4矩阵,且以一个直接了当的方式表示了矩阵中每一个元素的值。此外,在声明和运算之间添加空行,可以使得代码在视觉上能更好区分。

// 反例
public Matrix4f createOffsetMatrix(@NotNull Camera camera) {
    Matrix4f matrix = new Matrix4f(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
    matrix.translate(camera.position()).scale(1.0f, -1.0f, -1.0f).rotateYXZ(camera.rotation());
    return matrix;
}

// 正例
public Matrix4f createOffsetMatrix(@NotNull Camera camera) {
    Matrix4f matrix = new Matrix4f(
        1.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0f
    );

    matrix.translate(camera.position())
        .scale(1.0f, -1.0f, -1.0f)
        .rotateYXZ(camera.rotation());

    return matrix;
}

 

3、注释

首先,作者本人并不推崇在代码中使用过多注释,过多的注释会使得代码异常冗杂,且一段良好的程序应当做到自文档。除非是特殊的算数/算法实现,当一段代码需要额外多的注释进行描述时,通常意味着这段代码需要进行优化了。但当然,也不建议在项目中不添加任何注释,没用注释的代码如同天书,尽管代码拥有再好的自我解释性,也需要一定注释给继任者或未来的自己。给代码添加的注释需能够精准反应设计代码的逻辑及思想,并描述业务逻辑,使其他开发者能迅速了解到代码背后的含义。此外,注释力求精简准确、表达到位,如果代码本身已经描述得足够明确且清晰,则无需添加注释进行描述。

对类、类属性、方法的注释须使用JavaDoc规范,即 /** 内容 */ 格式,在例如IDE等工具中Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释,且不进入方法也可通过悬浮窗提示方法、参数、返回值的意义,提高阅读效率(原文)。对于所有的抽象方法(及接口中的方法),都应该使用JavaDoc注释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值