真實世界的 Go 設計模式 - 原型模式
原型模式是創建型模式的一種,其特點在於通過 “複製” 一個已經存在的實例來返回新的實例, 而不是新建實例。被複制的實例就是我們所稱的“原型”,這個原型是可定製的。
如果你有一個對象, 並希望生成與其完全相同或者類似的一個複製品, 你該如何實現呢? 首先, 你必須新建一個屬於相同類的對象,或者類似的對象, 然後你必須遍歷原始對象的所有成員變量, 並將成員變量值複製到新對象中。在 Go 生態圈中,我們常常使用下面的庫來做這份工作:
-
jinzhu/copier[1]: 張金柱提供的一個優秀的複製庫。
var ( user = User{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 200000} users = []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Salary: 100000}, {Name: "jinzhu 2", Age: 30, Role: "Dev", Salary: 60000}} employee = Employee{Salary: 150000} employees = []Employee{} ) copier.Copy(&employee, &user)
它是通過 reflect 反射的方式進行復制的,如果你的程序熱路徑上使用這個方法,你需要評估一下這種方式給性能帶來的影響。
-
switchupcb/copygen[2]:看 Readme 用起來就非常的複雜,不過它通過生成代碼的方式,而不是 reflect 方式,理論上來說性能應該更好一些。
-
jmattheis/goverter[3]:這個看起來比 copygen 好多了,我們只需需要定義一個轉換接口,然後讓它自動生成轉換的代碼。
當然你也可以自己實現轉換的方法,最簡單的方式就是:
var newValue = value
var newValue = *valuePointer
但是這種複製也有一些侷限性,比如在一些字段複製的值要進行調整,新的對象中有些字段不需要設置等等,所以一般我們有預見對象需要複製 (克隆) 的話,我們一般會爲這個對象類型實現Clone
方法,通過這種方式我們實現原型模式。
實際上,Go 標準庫有很多實現原型模式的例子。
strings.Clone
func Clone(s string) string
Clone 返回字符串 s 的一個全新副本。它保證將字符串 s 複製到一個新的分配空間中, 當只保留一個大字符串的一小部分子串時, 這可能非常重要。使用 Clone 可以幫助這些程序減少內存使用或者說避免內存泄露。
maps.Clone
func Clone[M ~map[K]V, K comparable, V any](m M "M ~map[K]V, K comparable, V any") M
Clone 返回 m 的一個副本。這是一個淺克隆: 新鍵和值是使用普通賦值設置的。
http.Transport.Clone
func (t *Transport) Clone() *Transport
Clone 返回 t 的導出字段的深度拷貝。
arena.Clone
func Clone[T any](s T "T any") T
Clone 方法返回一個 s 的淺拷貝,此拷貝的對象不再在 arena 中分配。
arena 包還不成熟
slog.Record.Clone
func (r Record) Clone() Record
Clone 返回一個沒有共享狀態的記錄副本。原記錄和克隆記錄都可以修改, 而不會相互影響。
此外,還有 Go 標準庫還有很多類似的實現原型模式的例子,比如:
-
cmd/distpack/archive.go: Archive.Clone
-
crypto/x509/cert_pool.go: CertPool.Clone
-
text/template/template.go: Template.Clone
-
html/template/template.go: Template.Clone
-
net/http/header.go: Header.Clone
-
net/http/request.go:Request.Clone
-
bytes.Clone
-
crypto/tls/common.go: Config.Clone
-
slices.Clone
還有一些非導出的方法和函數。
總的來說,如果你想實現原型模式,那麼最簡單的方式就是爲你的類型實現一個 Clone 方法,或者在你的包下實現一個 clone 函數。
參考資料
[1]
jinzhu/copier: https://github.com/jinzhu/copier
[2]
switchupcb/copygen: https://github.com/switchupcb/copygen
[3]
jmattheis/goverter: https://github.com/jmattheis/goverter
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/R1bPyZBMWizcfuwQv0-t8Q