Go 結構體與方法詳解
結構體基礎
Go 語言使用結構體(struct)作為組織和封裝資料的主要方式。相較於其他語言的類(Class),Go的結構體更加輕量且直觀。
分離檔案結構
在真實專案中,我們通常將結構體定義和主函數分開:
room.go
: 包含結構體定義和相關方法main.go
: 主程式,引用並使用結構體
這種分離可以提高程式的可維護性和可讀性。
結構體標籤詳解
在 room.go
中,我們看到這樣的定義:
1 | type Room struct { |
json:"id"
是結構體標籤(struct tag),它們是附加在結構體欄位上的元數據:
- 標籤格式: 反引號(`) 包裹的字串
- 標籤用途: 主要用於運行時反射機制
- 常見標籤:
json:"欄位名"
: 指定JSON序列化/反序列化時使用的欄位名db:"欄位名"
: 常用於資料庫ORMform:"欄位名"
: 常用於HTTP表單解析
標籤選項
標籤還可以包含選項,例如:
1 | Notes string `json:"notes,omitempty"` |
omitempty
選項表示:如果該欄位為零值(空字串、0、false等),則在JSON序列化時省略此欄位。
標籤的作用
當使用 json.Marshal()
序列化 Room
結構體時:
1 | room := Room{ID: 1, Number: "101"} |
輸出的JSON會根據標籤變成:
1 | {"id":1,"room_number":"101",...} |
而不是:
1 | {"ID":1,"Number":"101",...} |
這對API開發極其重要,可以保持JSON欄位名的一致風格,同時內部代碼可以遵循Go的命名規範。
值接收器方法詳解
在 room.go
中定義了幾個使用值接收器的方法:
1 | func (r Room) Info() string { |
讓我們深入理解值接收器方法:
值接收器的工作原理
- 接收器宣告:
(r Room)
表示此方法附加到Room
類型 - 參數傳遞: Go 使用值複製,方法內的
r
是原始結構體的副本 - 調用方式:
room.Info()
會複製room
到方法的r
參數
值接收器的特性
不改變原始值:
1
2
3
4
5
6
7func (r Room) SetAvailable(available bool) {
r.Available = available // 只改變副本
}
room := Room{Available: true}
room.SetAvailable(false)
fmt.Println(room.Available) // 仍然是 true方法接收者是副本:
每次調用方法都會創建結構體的完整副本,對於大型結構體可能影響性能適合不修改狀態的操作:
1
2
3func (r Room) CalculateTotalPrice(nights int) float64 {
return r.Price * float64(nights)
}方法鏈不可行:
由於操作的是副本,無法實現有效的方法鏈
值接收器的最佳實踐
使用於小型結構體:
結構體較小時,複製的開銷較低用於讀取操作:
1
2func (r Room) Info() string {...}
func (r Room) CalculateTotalPrice(nights int) float64 {...}不可變性設計:
值接收器方法保證不修改原結構體,提高程式可預測性線程安全考量:
每個線程操作的是獨立副本,避免共享狀態問題
使用案例分析
讓我們分析 Room
結構體的幾個值接收器方法:
Info() 方法
1 | func (r Room) Info() string { |
- 目的: 格式化輸出房間信息
- 為何使用值接收器: 純讀取操作,不需修改結構體
CalculateTotalPrice() 方法
1 | func (r Room) CalculateTotalPrice(nights int) float64 { |
- 目的: 計算特定住宿天數的總價
- 為何使用值接收器: 純計算,基於現有屬性,無需修改結構體
DaysSinceLastCleaning() 方法
1 | func (r Room) DaysSinceLastCleaning() int { |
- 目的: 計算自上次清潔以來的天數
- 為何使用值接收器: 純計算,不修改狀態
NeedsCleaning() 方法
1 | func (r Room) NeedsCleaning() bool { |
- 目的: 檢查房間是否需要清潔
- 為何使用值接收器: 基於其他方法的計算結果,不修改狀態