總結開源專案中的常見壞實踐(Bad Practice)

CoderV的進階筆記發表於2022-12-29

一些開源專案包含了各種程式設計的最佳實踐供人參考學習和借鑑。但是也有一些開源專案雖然初衷是好的。但是包含了一些程式碼的壞實踐。特別是對於一部分剛入行的大學生來說,可能會給到一些錯誤的示範。於是在此列舉一些專案中的壞實踐。

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年內的開發者參考使用的專案。

後端地址:
https://github.com/valarchie/AgileBoot-Back-End
鄙人能力水平有限,如果專案中發現不足或者錯誤,懇請指正。歡迎PR。一起構建一個規範完善的後端專案。

前端地址:
https://github.com/valarchie/AgileBoot-Front-End

鄙人前端小白,關於前端專案的規範以及最佳化僅作了力所能及的部分,還有很多最佳化空間。哪位前端大佬有興趣一起幫忙規範和最佳化嗎?

演示地址
www.agileboot.vip

歡迎加入全棧技術交流群:1398880

相關文章