程式設計實戰:如何管理程式碼裡的常量

華為雲開發者社群發表於2021-04-25
摘要:相信不少同學在見過千奇百怪的常量類之後都有這樣的疑問——怎麼管理這些常量類?

本文分享自華為雲社群《程式設計實戰:如何管理程式碼裡的常量》,原文作者:技術火炬手。

相信不少同學在見過千奇百怪的常量類之後都有這樣的疑問——怎麼管理這些常量類,但同時又覺得這個好像不是很重要,怎麼管理都能用的樣子,不就是個常量嘛,今天我們就把這個問題開啟來看看。

先看看前輩們是怎麼做的

從事Web開發,有個繞不開的常量就是HTTP的狀態碼,不如以此為起點

org.springframework.http.HttpStatus

①使用列舉

public enum HttpStatus {
    CONTINUE(100, "Continue"),
    SWITCHING_PROTOCOLS(101, "Switching Protocols"),
    PROCESSING(102, "Processing"),
    CHECKPOINT(103, "Checkpoint"),
    OK(200, "OK"),
    CREATED(201, "Created"),
    ACCEPTED(202, "Accepted"),
    NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"),
    NO_CONTENT(204, "No Content"),

org.eclipse.jetty.http.HttpStatus

②類中定義常量

public class HttpStatus {
    public static final int CONTINUE_100 = 100;
    public static final int SWITCHING_PROTOCOLS_101 = 101;
    public static final int PROCESSING_102 = 102;
    public static final int OK_200 = 200;
    public static final int CREATED_201 = 201;
    public static final int ACCEPTED_202 = 202;
    public static final int NON_AUTHORITATIVE_INFORMATION_203 = 203;
    public static final int NO_CONTENT_204 = 204;
    ...

org.apache.hc.core5.http.HttpStatus

③final類中定義常量

public final class HttpStatus {
    public static final int SC_INFORMATIONAL = 100;
    public static final int SC_CONTINUE = 100;
    public static final int SC_SWITCHING_PROTOCOLS = 101;
    public static final int SC_PROCESSING = 102;
    public static final int SC_EARLY_HINTS = 103;
    public static final int SC_SUCCESS = 200;
    public static final int SC_OK = 200;
    public static final int SC_CREATED = 201;
    public static final int SC_ACCEPTED = 202;
    public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    public static final int SC_NO_CONTENT = 204;
    ...

以上都是在常用開源框架中的定義,在業務中還曾見過一種用法,是

④在介面中定義常量

public interface HttpStatus {
    public static final int CONTINUE_100 = 100;
    public static final int SWITCHING_PROTOCOLS_101 = 101;
    public static final int PROCESSING_102 = 102;
    public static final int OK_200 = 200;
    public static final int CREATED_201 = 201;
    public static final int ACCEPTED_202 = 202;
    public static final int NON_AUTHORITATIVE_INFORMATION_203 = 203;
    public static final int NO_CONTENT_204 = 204;
    ...

這種介面被稱為常量介面,不過在《Effective Java》中的“介面只用於定義型別”這條建議中,特別批評了這種常量介面,認為“常量介面模式是對介面的不良使用”。原文敘述如下:

The constant interface pattern is a poor use of interfaces. That a class uses some constants internally is an implementation detail. Implementing a constant interface causes this implementation detail to leak into the class’s exported API. It is of no consequence to the users of a class that the class implements a constant interface. In fact, it may even confuse them. Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the constants, it still must implement the interface to ensure binary compatibility. If a nonfinal class implements a constant interface, all of its subclasses will have their namespaces polluted by the constants in the interface.
常量介面模式是使用介面的糟糕方式。類內部會使用一些常量,這是實現細節。然而,實現常量介面會導致這個實現細節洩漏到類的匯出 API 中。對於類的使用者來說,類實現一個常量介面沒有什麼價值。事實上,這甚至會讓他們感到困惑。更糟糕的是,它代表了一種承諾:如果在將來的版本中修改了類,使其不再需要使用常量,那麼它仍然必須實現介面以確保二進位制相容性。如果一個非 final 類實現了一個常量介面,那麼它的所有子類的名稱空間都會被介面中的常量所汙染。

考慮到常量類不應該被繼承,使用final要比沒有合適。

此外考慮到常量類應該不需要被例項化,將建構函式設定為private也是種額外的保護。

綜上,使用列舉final常量類就是首選了,那麼這兩者之間又如何抉擇呢?我們還是能從《Effective Java》得到有效的建議,第34條中就是在建議我們用列舉替代int常量。

如何管理常量類

雖然列舉是首選,但是常量類不會消失,常量不只是int常量,字串常量也是日常開發中避不開的,而且有些時候我們還是會覺得常量更為便捷。那該如何管理這些常量呢?

常量的壞味道

1. 巨幅常量類

把程式中所有用到的常量,都集中放到一個Constants類中。這樣做會有一些缺點,

  1. 為了增加常量會導致這個類的修改會很頻繁
  2. 依賴這個常量類的程式碼會很多
  3. 為了使用某一個常量,會導致引入很多無關的常量

2. 重複的常量定義

作為職場新人的時候,我常乾的一件事情就是重複定義常量,因為我不知道別人是否定義過,在哪裡定義過,與其要翻遍整個程式碼庫去找這個常量,自己定義一個顯得方便的多。

直到有一天我需要去修改一個常量的定義,前面欠下的債就需要還了。重複程式碼最大的問題就是,當你需要去修改或者維護那段重複的程式碼的時候,你需要去修改每一處,如果有遺漏,那就是bug了。重複常量也是如此。

管理思路

1. 定義多個功能單一的常量類

一個思路是按功能維度,去定義常量,一個常量類只跟某一功能相關。

如MySQLConstants、RedisConstants。

但這個有個很虛的概念,就是功能,一個功能可大可小,範疇可以是某一個類,也可以是一整個模組,這裡沒有定論,依賴的就是程式設計師聰明的頭腦和豐富的經驗了。

2. 不單獨定義常量類

另外一種思路就是不單獨設計常量類,常量在哪個類裡面使用,就把常量定義到這個類中。

比如在RedisClient和RedisConfig中都用到了之前RedisConstants中的常量,可以將其分別定義在RedisClient和RedisConfig中。

但這樣做有個明顯的問題就是,如果多個類用到了同一個常量,如果各自定義就造成了重複,如果定義一份宣告為public,就會造成兩個類之間的依賴。這樣來看,在沒有複用訴求的情況下,就地定義才比較可取。

3. 按層次複用

上述兩種方式都沒有完整的解決我們的疑惑,考慮將上述兩種方式結合起來,可以得到一種按層次定義複用的方式。

  1. 跨應用複用常量:放置在二方庫中,通常是client.jar中的constant目錄下。
  2. 應用內複用常量:放置在一方庫中,通常是common中的constant目錄下。
  3. 模組內部複用常量:即在當前子工程的constant目錄下。
  4. 包內複用常量:即在當前包下單獨的constant目錄下。
  5. 類內複用常量:直接在類內部private static final定義。

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章