从Java开源项目中总结出的常见坏实践(Bad Practice)

4,436 阅读6分钟

一些开源项目包含了各种编程的最佳实践供人参考学习和借鉴。但是也有一些开源项目虽然初衷是好的。但是包含了一些代码的坏实践。特别是对于一部分刚入行的大学生来说,可能会给到一些错误的示范。于是在此列举一些项目中的坏实践。

1.方法的用意判断是与否却返回字符串的“0”或者“1”

图片.png 如果一个方法明确返回是与否这两种情况,那么没有必要返回字符串的0或者1。这样会造成很多地方需要使用 字符串的形式来匹配结果判断是与否。例如以下这种形式。 图片.png 方法应该直接返回true或者false。 代码会简洁明了很多。

2.滥用三元运算符

图片.png

图片.png

3.滥用机翻英语

Poor有不好,差的意思。 例如My english is poor. 这里的Poor是差的意思。 但是下图这个方法getDatePoor。也用poor来表达获取时间“差”的含义。 英语还是程序员应该要掌握学习的。不能光高靠机翻英语,不然容易闹笑话。 图片.png

4.造不必要的大量轮子

很多方法或者功能我们应该尽可能的搜索是否已经有开源成熟的jar包或者框架实现。成熟的开源jar包或者框架,有大量完备的测试以及广泛的用户来确保质量。 如果实在需要自己造小轮子,请使用单元测试来确保质量

图片.png

5.大量if else语句

大量的if-else语句,具体情况具体分析。但是大部分都可以用卫语句提前返回结果。避免大量嵌套。

例如左边的写法可以改为右边的写法

图片.png

像下图这种情况可以用Stream Lambda来进行简化 图片.png

优化后

图片.png

6.多余的代码判断

有些时候可能会写出一些不必要的冗余判断

图片.png

7.大量的代码细节让阅读者增加心智负担

图片.png

我们应该封装一部分代码细节,暴露出代码的主流程,优化后

图片.png

8.繁琐的代码逻辑

图片.png 像上图这种情形,我们其实可以使用一句Stream 语句就可以描述出来。

9.数据库中是与否可以直接用tinyInt映射,不要用字符串来映射

这样会造成布尔字段取出时,还需要跟字符串1或者0进行比对,这是很尴尬的设计。 图片.png

10.异常捕获之后不做任何处理

我们捕获异常之后一般都需要使用log来记录错误情形,如果什么都不做,就很可能丢失错误信息,并且使代码排查过程更加困难。

图片.png

图片.png

11.使用Map填充数据

使用Java是静态语言,使用Map填充数据,反而失去了静态语言带来了代码检查以及IDEA识别字段引用的功能。

图片.png

12.混乱的常量

请不用将项目中所有的常量一股脑的放到一个类中。

图片.png

可以使用像这种静态类的方式,分门别类地放入不同的常量

图片.png

13.请使用驼峰命名

图片.png

14.变量的定义请在系统内保持一致,比如1在系统内表示是。 请勿有时表示是,有时表示否。

有时候用1表示肯定,有时候用0表示肯定,有时候用Y表示肯定。

图片.png

图片.png

图片.png

15.奇葩的代码脑回路

在外层方法判断一遍,在内层方法又进行一遍一样的判断

图片.png

16.常量随意的命名格式

常量的命名请使用大写加下划线的格式

图片.png

17.嵌套的Switch语句

就一种case了 完全没有必要使用switch语句 图片.png

18.使用魔法值

图片.png

例如项目中大量使用了“jpg”的字符串魔法值,使用魔法值使得我们无法统一找到代码的引用处。在重构的时候难免会有疏漏。

图片.png

19.单个方法代码超过80行

如果单个方法的代码行超过80行,意味你的代码缺乏封装和可读性。例如这种一大坨的代码。

public static void initColumnField(GenTableColumn column, GenTable table)
{
    String dataType = getDbType(column.getColumnType());
    String columnName = column.getColumnName();
    column.setTableId(table.getTableId());
    column.setCreateBy(table.getCreateBy());
    // 设置java字段名
    column.setJavaField(StringUtils.toCamelCase(columnName));
    // 设置默认类型
    column.setJavaType(GenConstants.TYPE_STRING);
    column.setQueryType(GenConstants.QUERY_EQ);

    if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType))
    {
        // 字符串长度超过500设置为文本域
        Integer columnLength = getColumnLength(column.getColumnType());
        String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
        column.setHtmlType(htmlType);
    }
    else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType))
    {
        column.setJavaType(GenConstants.TYPE_DATE);
        column.setHtmlType(GenConstants.HTML_DATETIME);
    }
    else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType))
    {
        column.setHtmlType(GenConstants.HTML_INPUT);

        // 如果是浮点型 统一用BigDecimal
        String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
        if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0)
        {
            column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
        }
        // 如果是整形
        else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10)
        {
            column.setJavaType(GenConstants.TYPE_INTEGER);
        }
        // 长整形
        else
        {
            column.setJavaType(GenConstants.TYPE_LONG);
        }
    }

    // 插入字段(默认所有字段都需要插入)
    column.setIsInsert(GenConstants.REQUIRE);

    // 编辑字段
    if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk())
    {
        column.setIsEdit(GenConstants.REQUIRE);
    }
    // 列表字段
    if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk())
    {
        column.setIsList(GenConstants.REQUIRE);
    }
    // 查询字段
    if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk())
    {
        column.setIsQuery(GenConstants.REQUIRE);
    }

    // 查询字段类型
    if (StringUtils.endsWithIgnoreCase(columnName, "name"))
    {
        column.setQueryType(GenConstants.QUERY_LIKE);
    }
    // 状态字段设置单选框
    if (StringUtils.endsWithIgnoreCase(columnName, "status"))
    {
        column.setHtmlType(GenConstants.HTML_RADIO);
    }
    // 类型&性别字段设置下拉框
    else if (StringUtils.endsWithIgnoreCase(columnName, "type")
            || StringUtils.endsWithIgnoreCase(columnName, "sex"))
    {
        column.setHtmlType(GenConstants.HTML_SELECT);
    }
    // 图片字段设置图片上传控件
    else if (StringUtils.endsWithIgnoreCase(columnName, "image"))
    {
        column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);
    }
    // 文件字段设置文件上传控件
    else if (StringUtils.endsWithIgnoreCase(columnName, "file"))
    {
        column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);
    }
    // 内容字段设置富文本控件
    else if (StringUtils.endsWithIgnoreCase(columnName, "content"))
    {
        column.setHtmlType(GenConstants.HTML_EDITOR);
    }
}

20.避免使用反逻辑

图片.png

像图中!(x == 0) 可以直接改成x != 0 即可

还有以下这种冗余代码。

图片.png

21.复杂的判断使用有意义的变量来替代

图片.png

比如上图的判断我们可以用一个变量 isLongField来替代 提高代码的可读性。

22.常量没有使用final来修饰

如果没有使用final来修饰的话,就有可能在代码中被修改。

图片.png

23.字符编码直接用字符串表示

图片.png

字符编码,JDK中都有常量可以直接表示,我们可以直接使用

图片.png

24.多余的方法修饰符

Java中interface类,方法默认都是Public的,没必要再加上public修饰符

图片.png

25.不必要的ToString

图片.png

图片.png

26.多余的变量声明

如果变量声明之后没有做任何处理,请直接通过return返回,不要多声明一个变量

图片.png

27.使用语义不清晰的方法

例如String的indexOf方法 我们完全可以使用contains方法来替代,使代码的语义更一目了然。

图片.png

28.毫无必要的包装语句unboxing和boxing

Integer.valueOf

返回的本身就是int, 没有必要再调用intValue方法 图片.png

29.使用+=进行在循环中字符串拼接

图片.png

+=会造出临时的字符串,我们应该使用StringBuilder在循环中拼接字符串

以上就是总结的关于项目中的一些坏实践,请大家务必使用。有其他坏实践,恳请大家继续补充。


鄙人在业余时间弄了一个全栈项目Agileboot,初衷是想做一个代码规范,项目结构良好,可供大学生或者入门3年内的开发者参考使用的项目。

后端地址: github.com/valarchie/A…
鄙人能力水平有限,如果项目中发现不足或者错误,恳请指正。欢迎PR。一起构建一个规范完善的后端项目。

前端地址: github.com/valarchie/A…

鄙人前端小白,关于前端项目的规范以及优化仅作了力所能及的部分,还有很多优化空间。哪位前端大佬有兴趣一起帮忙规范和优化吗?

演示地址 www.agileboot.vip

欢迎加入全栈技术交流群:1398880