3 回答

TA貢獻1886條經驗 獲得超2個贊
由于A是不變的,因此這非常適合函數,而不是字段。
type room struct {
L int
W int
}
func (r *room) area() int {
return r.L * r.W
}

TA貢獻1829條經驗 獲得超13個贊
如果您想將 A 保留為字段,您可以選擇在構造函數中執行計算。
type room struct {
L int
W int
A int
}
func newRoom(length, width, int) room {
return room{
L: length,
W: width,
A: length * width,
}
}

TA貢獻1796條經驗 獲得超7個贊
如果您考慮一下您的目標,您會發現基本上您希望“不添加不必要的代碼”實際上是不手動編寫任何代碼,而不是不執行任何代碼:當然,如果類型定義
type room struct {
?L int
?W int
?A int = room.L*room.H
}
在 Go 中是可能的,這意味著 Go 編譯器會做出安排,而不是像這樣的任何代碼
var r room
r.L = 42
以隱式變異的方式編譯r.A。
換句話說,編譯器必須確保對程序中任何類型的變量的任何L一個或多個字段的任何修改也會執行計算并更新每個此類變量的字段。WroomA
這帶來了幾個問題:
如果你的公式比較棘手,比如,怎么辦
A int = room.L/room.W
?首先,考慮到類型 0 值的隨意 Go 規則
int
,無辜的聲明var r room
會立即使程序崩潰,因為編譯器插入的代碼執行整數除以零以強制討論不變量。其次,即使我們發明了一個不計算公式的可疑規則(在 Go 中,這也是初始化),問題仍然存在:在以下場景中會發生什么?
var?r?room r.L?=?42
正如您所看到的,即使編譯器不會使程序在第一行崩潰,它也必須在第二行進行安排。
當然,我們可以添加另一個有問題的規則來回避問題:要么以某種方式將每個字段“標記”為“顯式設置”,要么要求用戶為此類“武裝”有“公式”的類型提供顯式“構造函數”。這兩種解決方案都有其各自的缺點:跟蹤寫入字段訪問會帶來性能成本(某些字段現在有一個隱藏標志,會占用空間,并且每次訪問此類字段都會花費額外的 CPU 計數),而使用構造函數又成為基石原則之一Go 設計的核心理念:盡可能少地使用魔法。
該公式創建隱藏寫入。
直到你開始為它所擅長的任務編寫“更硬核”的 Go 程序(具有大量同時工作的 goroutine 的高度并發代碼)時,這一點可能并不明顯,但是當你這樣做時,你不得不考慮共享狀態及其實現方式突變,并因此影響這些突變同步以保持程序正確的方式。
因此,假設我們使用互斥鎖來保護對
W
或 的訪問;鑒于互斥操作是顯式的(即程序員顯式編碼鎖定/解鎖操作),L
編譯器如何確保 的 突變也受到保護?A
(這個問題與前一個問題有些相關。)如果“公式”做了“有趣的事情”——例如訪問/改變外部狀態,該怎么辦?
這可以是任何事情,從訪問全局變量到查詢數據庫,再到使用文件系統,再到通過 IPC 或網絡協議進行交換。
這一切看起來可能非常天真,就像
A int = room.L * room.W * getCoefficient()
所有漂亮的細節都隱藏在該getCoefficient()
調用中一樣。當然,我們可以再次通過對編譯器施加任意限制來解決這個問題,只允許顯式訪問相同封閉類型的字段,并且只允許它們參與沒有函數調用或某些“白名單”的簡單表達式。它們的子集,例如
math.Abs
或其他。這顯然降低了該功能的實用性,同時使語言變得更加復雜。如果“公式”具有非線性復雜性怎么辦?
假設,該公式是
O(N3)
關于 的值W
。然后,將
W
值設置為 0 幾乎會立即處理,但將其設置為 10000 會顯著減慢程序速度,并且這兩種結果都會形成看似不太不同的語句:r.W = 0
vs?r.W = 10000
。這又違背了盡可能少用魔法的原則。
為什么我們只允許在結構類型上使用這樣的東西,而不是在任意變量上——假設它們都在相同的詞法范圍內?
這看起來像是另一個任意限制。
另一個(據稱)最明顯的問題是,當程序員像這樣時會發生什么
var r room
r.L = 2? // r.A is now 2×0=0
r.W = 5? // r.A is now 2×5=10
r.A = 42 // The invariant r.A = r.L×r.W is now broken
?
現在您可以看到,上述所有問題都可以通過簡單地編寫您需要的內容來解決,例如使用以下方法:
// use "unexported" fields
type room struct {
?l int
?w int
?a int
}
func (r *room) SetL(v int) {
? r.l = v
? updateArea()
}
func (r *room) SetW(v int) {
? r.w = v
? updateArea()
}
func (r *room) GetA() int {
? return r.a
}
func (r *room) updateArea() {
? r.a = r.l * r.w
}
通過這種方法,您可能對上述所有問題都一清二楚。
請記住,程序是為人類閱讀而編寫的,然后才供機器執行;對于正確的軟件工程來說,最重要的是盡可能保留代碼,而代碼的各個部分之間盡可能不存在任何魔法或復雜的隱藏依賴關系。請記住
軟件工程是當你增加時間和其他程序員時編程所發生的事情。
- 3 回答
- 0 關注
- 180 瀏覽
添加回答
舉報