<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
不變性的概念非常簡單,在您建立結構體後,就永遠無法修改它。這個概念聽起來非常簡單,但您的程式想利用它從中收益並不是那麼容易。接下來我們在 Go 中,使用不變性概念,來讓您的程式碼更具有可讀性和穩定性。
當我們使用相同的引數,執行相同的函數兩次,我們的預期,應該得到相同的結果。但是當我們的函數中依賴外部狀態或全域性變數時,函數可能會輸出不同的結果。我們最好避免這種情況。
函數的引數總是給定的,那我們呼叫,總是可以返回相同的函數。如果您有一個共用全域性變數用於函數內部的某些內容,請考慮將該變數作為引數傳遞,而不是直接函數內部使用它。
這可以讓您的函數返回值更加可預測,並且更加易於測試,整個程式碼的可讀性也會得到提高,因為呼叫者會知道,哪些值會影響函數的行為,引數的作用不就是會影響返回值的嗎?
讓我們看一個例子。
package main import ( "fmt" "math/rand" "time" ) var randNum int func main() { s1 := rand.NewSource(time.Now().UnixNano()) r1 := rand.New(s1) randNum = r1.Intn(100) fmt.Println(Add(1, 1)) } func Add(a, b int) int { return a + b + randNum }
Add
函數中使用了全域性變數 randNum
作為計算的一部分,從函數簽名中並沒有體現這一點。更好的方法是,全域性變數 randNum
應該作為引數傳遞,如下所示。
func Add(a, b, randNum int) int { return a + b + randNum }
這樣更具有可預測性,而且我們如果需要修改入參,影響的作用域也僅在 Add
函數中。
我們知道,Go
結構體中的成員變數,如果首字母為大寫,那麼該成員變數對外可見(這是編譯器決定的)。回到我們的部落格,僅匯出結構體函數,而不是成員變數,目的是希望成員變數的資料被保護,保證成員變數的有效的狀態!因為這可以讓您的程式碼更加可靠,您不必維護每個修改該成員變數的操作,因為這些操作都將無效。
舉一個例子
ackage main import ( "fmt" ) type AK47 struct { bullet int } func NewAK47(bullet int) AK47 { return AK47{bullet: bullet} } func (a AK47) GetBullet() int { return a.bullet } func (a AK47) SetBullet(bullet int) { a.bullet = bullet } func main() { ak47 := NewAK47(30) fmt.Println(ak47.GetBullet()) ak47.SetBullet(20) fmt.Println(ak47.GetBullet()) }
我們定義了一個結構體 AK47
,這把槍有一個成員變數 bullet
子彈數,它是非匯出欄位,我們還定義了一個建構函式 NewAK47
和一個 GetBullet
函數。
一旦建立了 AK47
,就無法更改它的成員變數 bullet
了。此時您可能會有疑惑,如果我們需要修改成員變數呢?別急,您可以試試下面的方法。
在上一個副標題中,我們提到了一個概念,在建立結構體後永遠不要更改它。然而在實際中,我們經常需要修改結構體中的成員變數。
我們在使用不變性的同時,仍然可以維護範例化結構體的多個狀態,這並不意味著我們打破了結構體建立後不要更改它,我們更改的是它的副本,也就是複製後的結構體。複製後的結構體?難道我們需要去實現很多複製結構體每個欄位的函數嗎?
當然不,我們可以利用 Go
的特性,在呼叫函數時,入參是複製值的行為。對於需要修改結構體中成員變數的操作,我們可以建立一個函數,該函數接收結構體為引數,並且返回一個修改後的結構體副本。
我們可以在不改變呼叫方結構體的情況下,修改該副本的任何內容,這意味著對於原結構體沒有任何副作用,並且該結構體的值仍然是可預測的。
不知道您有沒有用過 Go
標準庫的 Slice
切片,其中的 append
函數就使用了這個方法。讓我們接著用 AK47
來實現這個方法
程式碼如下
package main import ( "fmt" ) type AK47 struct { bullet int } func NewAK47(bullet int) AK47 { return AK47{bullet: bullet} } func (a AK47) GetBullet() int { return a.bullet } func (a AK47) AddBullet(ak47 AK47) AK47 { newAK47 := NewAK47(a.GetBullet() + ak47.GetBullet()) return newAK47 } func main() { ak47 := NewAK47(30) add := NewAK47(20) fmt.Println(ak47.GetBullet()) ak47 = ak47.AddBullet(add) fmt.Println(ak47.GetBullet()) }
如您所見,我們通過 AddBullet
函數增加槍的子彈,但實際上並沒有更改傳入的結構體中的任何成員變數。最後,返回了一個帶有更新欄位的新 AK47
結構體。
與複製值相比,指標更有優勢,尤其是當您的結構體成員變數、內容非常大時時,這種方法,通過複製的方式修改資料,可能會導致效能問題。您應該問自己,這麼做是否值得,例如您正在編寫並行程式碼?
您在使用不變數時,請務必先權衡利弊。實現本篇部落格中所描述的方法,需要大量的程式碼。但是,如果我們在編寫並行程式碼時,不考慮共用變數的不可變性,往往會出現與預期不符的情況,例如記憶體競態問題?其實我想說的就是執行緒安全問題 : - )
實現不變性,也可能出現嚴重的效能問題!這是一把雙刃劍。請不要過早的優化程式碼。
以上就是Go通過不變性優化程式詳解的詳細內容,更多關於Go 程式不變性的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45