3

真实世界的Go设计模式 - 原型模式

 1 year ago
source link: https://colobu.com/2023/08/07/go-design-pattern-prototype/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

真实世界的Go设计模式 - 原型模式

*原型模式(Prototype Pattern)是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。

如果你有一个对象,并希望生成与其完全相同或者类似的一个复制品,你该如何实现呢?首先你必须新建一个属于相同类的对象,或者类似的对象,然后你必须遍历原始对象的所有成员变量,并将成员变量值复制到新对象中。在Go生态圈中,我们常常使用下面的库来做这份工作。

  • jinzhu/copier: 张金柱提供的一个优秀的复制库。
  • switchupcb/copygen: 看Readme用起来就非常的复杂,不过它通过生成代码的方式,而不是reflect方式,理论上来说性能应该更好一些。
  • jmattheis/goverter: 这个看起来比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

Clone返回m的一个副本。这是一个浅克隆:新键和值是使用普通赋值设置的。

http.Transport.Clone

func (t *Transport) Clone() *Transport

Clone返回t的导出字段的深度拷贝。

arena.Clone

func Clone[T any](s T) 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函数。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK