2.4 常數與 typed string
2.4 常數與 typed string
常數讓程式中的固定值有名稱。typed string 則讓一組字串值形成語意邊界,避免任意字串到處流動。
本章目標
學完本章後,你將能夠:
- 用
const定義固定值 - 理解 untyped constant 與 typed constant 的差異
- 用 typed string 表達狀態與事件類型
- 集中管理協定字串與 log message
【觀察】字串散落會增加維護成本
協定字串的核心問題是:同一個語意若以裸字串散落在程式中,拼字、修改與合法值判斷都會變成隱性成本。以下是裸字串散落的典型樣子:
1if status == "active" {
2 // ...
3}
4
5if eventType == "user.created" {
6 // ...
7}
8
9log.Println("user created")短期看起來很直接,但問題是:
- 拼字錯誤不容易被發現
- 修改字串時要全專案搜尋
- 無法從型別看出哪些值是合法的
- 不同概念可能共用同一種
string
常數可以先解決命名與集中管理問題。
【判讀】const 是把意圖寫進名稱
const 的核心用途是把固定值的意圖寫進名稱。Go 的常數宣告如下:
1const DefaultPort = 8080
2const EventUserCreated = "user.created"使用常數後,呼叫端讀到的是語意:
1if eventType == EventUserCreated {
2 // ...
3}這比直接看到 "user.created" 更清楚,因為名稱說明了這個字串在系統中的角色。
【策略】用 typed string 區分概念
typed string 的核心用途是用型別區分不同語意的字串。當多組資料底層都是字串,但語意不同,可以定義不同型別:
1type TaskStatus string
2
3const (
4 TaskStatusPending TaskStatus = "pending"
5 TaskStatusRunning TaskStatus = "running"
6 TaskStatusDone TaskStatus = "done"
7 TaskStatusFailed TaskStatus = "failed"
8)函式簽名可以明確要求 TaskStatus:
1func CanRetry(status TaskStatus) bool {
2 return status == TaskStatusFailed
3}這不會讓 Go 變成 enum 語言,但能讓 API 更清楚。讀者看到 TaskStatus,就知道這不是任意字串。
【執行】事件類型與 action 常數
事件類型的核心規則是:同一組事件值應集中在同一個 typed string 型別下。事件驅動程式常需要管理事件類型:
1type EventType string
2
3const (
4 EventUserCreated EventType = "user.created"
5 EventUserUpdated EventType = "user.updated"
6 EventUserDeleted EventType = "user.deleted"
7)API action 也可以用同樣方式:
1type Action string
2
3const (
4 ActionSubscribe Action = "subscribe"
5 ActionUnsubscribe Action = "unsubscribe"
6 ActionPing Action = "ping"
7)處理時,switch 會變得可讀:
1func HandleAction(action Action) error {
2 switch action {
3 case ActionSubscribe:
4 return nil
5 case ActionUnsubscribe:
6 return nil
7 case ActionPing:
8 return nil
9 default:
10 return fmt.Errorf("unknown action: %s", action)
11 }
12}log message 也適合集中
log message 的核心規則是:會被 grep、監控或文件引用的訊息應保持穩定。這類 message 可以用常數集中:
1const (
2 LogServerStarted = "server started"
3 LogEventDropped = "event dropped"
4 LogInvalidAction = "invalid action"
5)這樣做的價值是讓 log 訊號穩定。當 log 是除錯入口時,穩定字串就是系統 contract 的一部分。
常見取捨
不必把所有字串都變常數
只出現一次、沒有協定意義、不需要搜尋的文字,可以直接寫在原處。過度常數化會讓讀者一直跳檔案。
常數名稱要說明清楚概念
如果常數名稱只是重複值本身,幫助不大:
1const StringActive = "active" // 不佳比較好的名稱要說明概念:
1const TaskStatusActive TaskStatus = "active"