3 回答

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

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