*原型模式(Prototype Pattern)是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。
如果你有一个对象,并希望生成与其完全相同或者类似的一个复制品,你该如何实现呢?首先你必须新建一个属于相同类的对象,或者类似的对象,然后你必须遍历原始对象的所有成员变量,并将成员变量值复制到新对象中。在Go生态圈中,我们常常使用下面的库来做这份工作。
- jinzhu/copier: 张金柱提供的一个优秀的复制库。
- switchupcb/copygen: 看Readme用起来就非常的复杂,不过它通过生成代码的方式,而不是reflect方式,理论上来说性能应该更好一些。
- jmattheis/goverter: 这个看起来比copygen好多了,我们只需需要定义一个转换接口,然后让它自动生成转换的代码。
当然你也可以自己实现转换的方法,最简单的方式就是:
|
|
但是这种复制也有一些局限性,比如在一些字段复制的值要进行调整,新的对象中有些字段不需要设置等等,所以一般我们有预见对象需要复制(克隆)的话,我们一般会为这个对象类型实现Clone
方法,通过这种方式我们实现原型模式。
实际上,Go标准库有很多实现原型模式的例子。
strings.Clone
|
|
Clone返回字符串s的一个全新副本。它保证将字符串s复制到一个新的分配空间中,当只保留一个大字符串的一小部分子串时,这可能非常重要。使用Clone可以帮助这些程序减少内存使用或者说避免内存泄露。
maps.Clone
|
|
Clone返回m的一个副本。这是一个浅克隆:新键和值是使用普通赋值设置的。
http.Transport.Clone
|
|
Clone返回t的导出字段的深度拷贝。
arena.Clone
|
|
Clone方法返回一个s的浅拷贝,此拷贝的对象不再在arena中分配。
arena 包还不成熟
slog.Record.Clone
|
|
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函数。