5 回答

TA貢獻1875條經驗 獲得超5個贊
我解決這個問題的方法是只使用NewPerson(params)而不是導出這個人。這使得獲取person實例的唯一方法是通過您的New方法。
package person
// Struct is not exported
type person struct {
Name string
Age int
Gender bool
}
// We are forced to call the constructor to get an instance of person
func New(name string, age int, gender bool) person {
return person{name, age, gender}
}
這迫使每個人都從同一個地方獲得一個實例。當您添加一個字段時,您可以將它添加到函數定義中,然后在它們構造新實例的任何地方都會出現編譯時錯誤,因此您可以輕松找到它們并修復它們。

TA貢獻1796條經驗 獲得超4個贊
首先,您的copyPerson()函數名不副實。它復制a 的一些字段Person,但不是(必須)全部。它應該被命名為copySomeFieldsOfPerson().
要復制完整的結構值,只需分配結構值。如果你有一個接收非指針的函數Person,那已經是一個副本,所以只需返回它的地址:
func copyPerson(p Person) *Person {
return &p
}
就是這樣,這將復制所有現在和未來的領域Person。
現在可能存在字段是指針或類似標題的值(如切片)的情況,應該從原始字段(更準確地說是從指向的對象)“分離”,在這種情況下,您確實需要進行手動調整,例如
type Person struct {
Name string
Age int
Data []byte
}
func copyPerson(p Person) *Person {
p2 := p
p2.Data = append(p2.Data, p.Data...)
return &p2
}
或者不制作另一個副本p但仍然分離的替代解決方案Person.Data:
func copyPerson(p Person) *Person {
var data []byte
p.Data = append(data, p.Data...)
return &p
}
當然,如果有人添加了一個也需要手動處理的字段,這將無濟于事。
您還可以使用未加密的文字,如下所示:
func copyPerson(p Person) *Person {
return &Person{
p.Name,
p.Age,
}
}
如果有人向 中添加新字段,這將導致編譯時錯誤Person,因為未鍵控的復合結構文字必須列出所有字段。同樣,如果有人更改了可將新字段分配給舊字段的字段(例如,有人交換了具有相同類型的彼此相鄰的 2 個字段),這將無濟于事,而且不鼓勵使用未加密的文字。
包所有者最好在Person類型定義旁邊提供一個復制構造函數。因此,如果有人發生變化Person,他/她應該負責保持CopyPerson()運營。正如其他人提到的,你應該已經有了單元測試,如果CopyPerson()不符合它的名字,它應該會失敗。
最佳可行方案?
如果您不能將CopyPerson()next 放在類型旁邊Person并讓其作者維護它,請繼續進行結構值復制和手動處理指針和類似標頭的字段。
您可以創建一個person2類型,它是該Person類型的“快照”。如果原始類型發生變化,使用空白全局變量接收編譯時警報Person,在這種情況下,copyPerson()包含 的源文件將拒絕編譯,因此您將知道它需要調整。
這是可以做到的:
type person2 struct {
Name string
Age int
}
var _ = Person(person2{})
Person如果和的字段person2不匹配,則空白變量聲明將不會編譯。
上述編譯時檢查的一種變體可能是使用類型化nil指針:
var _ = (*Person)((*person2)(nil))

TA貢獻1824條經驗 獲得超6個贊
慣用的方法是根本不這樣做,而是使零值有用。復制函數的例子并沒有真正意義,因為它完全沒有必要——你可以說:
copy?:=?new(Person) *copy?=?*origPerson
不需要專門的功能,也不必保持字段列表是最新的。如果你想要一個新實例的構造函數NewPerson
,就像理所當然的那樣,只需編寫一個并使用它。Linters 在某些方面非常有用,但沒有什么能比得上廣為人知的最佳實踐和同行代碼審查。

TA貢獻1827條經驗 獲得超9個贊
我不知道強制執行該規則的語言規則。
也就是說,我會重新考慮這里的設計。如果Person
struct 在您的代碼庫中如此重要,請集中創建和復制它,以便“遙遠的地方”不只是創建和移動Person
s。重構您的代碼,以便僅使用一個構造函數來構建Person
s(可能類似于person.New
返回 a?person.Person
),然后您將能夠集中控制其字段的初始化方式。

TA貢獻1784條經驗 獲得超2個贊
我能想出的最好的解決方案(而且不是很好)是定義一個與tempPerson該結構相同的新結構Person,并將其放在任何初始化新 Person 結構的代碼附近,并更改初始化一個Person以便它改為將其初始化為 atempPerson但隨后將其轉換為 a Person。像這樣:
type tempPerson struct {
Name string
Age int
[some other fields]
}
func copyPerson(origPerson Person) *Person {
tempCopy := tempPerson{
Name: orig.Name,
Age: orig.Age,
[some other fields]
}
copy := (Person)(tempCopy)
return ©
}
這樣,如果將另一個字段Gender添加到Person但不添加到tempPerson代碼中,將在編譯時失敗。據推測,開發人員然后會看到錯誤,編輯tempPerson以匹配他們對 的更改Person,并在這樣做時注意到附近的代碼使用tempPerson并認識到他們應該編輯該代碼以也處理該Gender字段。
我不喜歡這個解決方案,因為它涉及在我們初始化結構Person并希望獲得這種安全性的任何地方復制和粘貼結構定義。有沒有更好的辦法?
- 5 回答
- 0 關注
- 203 瀏覽
添加回答
舉報