原型模式补遗 - Go语言中的深度拷贝

在前一篇真实世界的Go设计模式 - 原型模式一文中,我们介绍了gorm作者张金柱的另一个库jinzhu/copier,它提供了对象数据深度复制的能力。

在和网友的交流中,有网友提到金柱的那个库不支持any类型的深度复制,也提到也有叫做DeepCopy库支持,但又不支持指针深度拷贝。真的是这么样,或者说当前还有这种现象么?我写了一个测试程序,分别对指针、map、slice和接口进行了测试,发现这些库其实都支持了各种类型的深度复制。代码太长我就不专门贴在这里了,大家可以访问下面的网址查看测试代码:https://go.dev/play/p/WoringjBHeZ .

当然,对于jinzhu/copier,你需要显示指定深度拷贝参数,否则使用默认的Copy还是不能够复制接口、map等对象。jinzhu/copier还提供不同类型的对象之间的复制,功能更强大,代码也就更复杂。

这些深度拷贝的库如下:

相比较而言,mohae/deepcopy的代码也更简洁易懂,也被将近一万多个github项目锁使用。
唯一比较遗憾的是,这个项目将近6年没有更新了,一方面说明稳定,另一方面也说明Go的一些新的特性也没有应用在它的方面,设置它连个go.mod都没有。

我复制了这个项目,准备维护下去。你可以关注 smallnest/deepcopy

目前我为它增加了泛型的支持, 避免了手工的type assertion,也增加channel的复制能力。

1
cpy := deepcopy.Copy[T](orig)

所以一个完整的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
complexData := ComplexStruct{
ID: 1,
Name: "Complex Object",
Description: "A struct with various types for JSON benchmarking",
IsEnabled: true,
Count: 5,
Price: 99.99,
Tags: []string{"tag1", "tag2", "tag3"},
CreatedAt: "2023-08-07T12:34:56Z",
Options: map[string]bool{"option1": true, "option2": false, "option3": true},
Nested: NestedStruct{
Field1: 42,
Field2: "Nested Field",
ItemAStruct: ItemAStruct{
ItemID: 100,
ItemName: "Item A",
},
ItemBStruct: &ItemBStruct{
ItemType: "Item Type",
ItemFrom: "Item From",
},
},
Items: []ItemStruct{
{ItemID: 101, ItemName: "Item 1"},
{ItemID: 102, ItemName: "Item 2"},
},
ExtraData: json.RawMessage(`{"key": "value"}`),
Handler: func(w http.ResponseWriter, r *http.Request) {},
Opts: map[string]any{
"opt1": "value1",
"opt2": 2,
"opt3": true,
},
}
newData := Copy[ComplexStruct](complexData)

channel类型只复制channel类型和它的容量,并没有复制channel中的元素:一是并没有方式直接获取缓存在channel中的元素,二是channel中的元素总是在变化之中,复制过来的元素也许在channel中已经被取走了。它也没有复制channel的状态,原channel被close了,复制的新channel也没有被close。