文章目录
四、prototype 原型模式
https://refactoringguru.cn/design-patterns/prototype
如果希望 复制对象, 可使用 “prototype 模式”
如果 “待复制的对象” 是 interface 而不是 class, 或者如果 class 有 private 变量时. 无法知道 "待复制的对象"的细节, 则需要其实现 “clone()” 方法供外部调用.
源码详见:godp/04prototype at master · datager/godp · GitHub
4.1 inode
本例希望实现文件系统的复制功能. 数据结构是 inode 接口, file 和 folder 都实现了该接口. 详见 https://refactoringguru.cn/design-patterns/prototype/go/example
当然, 另一条路是: 也可以直接用序列化+反序列化实现复杂对象的 clone()
4.1.1 inode_test
package _41inode
import "testing"
func TestInode(t *testing.T) {
d1 := &directory{
name: "json",
children: []inode{&file{name: "a.json"}, &file{name: "b.json"}},
}
d2 := &directory{
name: "yaml",
children: []inode{&file{"c.yaml"}, &file{"d.yaml"}},
}
f1 := &file{name: "e.txt"}
f2 := &file{name: "f.sql"}
directoryHome := directory{
name: "/home",
children: []inode{d1, d2, f1, f2},
}
directoryHome.print(printIndent)
cp := directoryHome.clone()
cp.print(" ")
}
// code result
=== RUN TestInode
/home
json
a.json
b.json
yaml
c.yaml
d.yaml
e.txt
f.sql
/home_clone
json_clone
a.json_clone
b.json_clone
yaml_clone
c.yaml_clone
d.yaml_clone
e.txt_clone
f.sql_clone
--- PASS: TestInode (0.00s)
PASS
4.1.2 inode
package _41inode
// inode 是文件系统的节点
type inode interface {
// 打印此节点的信息, indent 是缩进符(如\t)
print(indent string)
// 复制此节点
clone() inode
}
const printIndent = " "
4.1.3 file
package _41inode
import "fmt"
type file struct {
// 文件名
name string
}
func (f *file) print(indent string) {
str := indent + f.name
fmt.Println(str)
}
func (f *file) clone() inode {
return &file{name: f.name + "_clone"}
}
4.1.4 directory
package _41inode
import (
"fmt"
)
type directory struct {
// 目录名
name string
// 子节点
children []inode
}
func (d *directory) print(indent string) {
fmt.Println(indent + d.name)
for _, child := range d.children {
child.print(indent + printIndent) // 在基础 indent 的基础上, 再添加 printIndent
}
}
func (d *directory) clone() inode {
children := make([]inode, 0)
for _, child := range d.children {
children = append(children, child.clone())
}
cp := &directory{
name: d.name + "_clone",
children: children,
}
return cp
}
4.2 UserSetting
创建对象经常是资源密集且耗时的, 尤其当需要从外部(数据库, 磁盘, 网络)获取时, prototype 模式就是为了解决此问题.
通过复制一个已有的对象, 来创建新对象, 而不是从头开始.
本例, 以一个 用户的设置为例. 当我们希望新建一个用户的设置时, 可以基于已有的设置做修改.
详见: https://vocus.cc/article/64eb8144fd897800016e0601
04prototype/042user_setting
├── readme.md
├── user_setting.go
└── user_setting_test.go
### 4.2.1 user_setting_test.go
package _42user_setting
import "testing"
func TestUserSettingClone(t *testing.T) {
u := &UserSetting{
Theme: "Atom Dark",
Font: "14px",
Colors: []string{"blue", "green", "white"},
Layouts: map[string]string{"admin": "full", "test": "empty"},
}
// 克隆并修改
uc := u.Clone()
uc.Theme = "Darcula"
uc.Font = "12px"
}
4.2.2 user_setting.go
package _42user_setting
// UserSetting 用户的设置, 比如和展示相关
type UserSetting struct {
Theme string
Font string
Colors []string
Layouts map[string]string
}
// Clone 复制对象, 需要用深拷贝实现
func (u *UserSetting) Clone() *UserSetting {
colors := make([]string, 0)
for _, c := range u.Colors {
colors = append(colors, c)
}
layouts := make(map[string]string)
for id, layout := range u.Layouts {
layouts[id] = layout
}
return &UserSetting{
Theme: u.Theme,
Font: u.Font,
Colors: colors,
Layouts: layouts,
}
}
文章介绍了在Go语言中如何使用Prototype模式实现inode、file和UserSetting对象的复制,包括接口实现、clone方法以及如何在UserSetting中进行深拷贝。通过实例展示了如何在目录结构和用户设置场景中应用这种设计模式。
949

被折叠的 条评论
为什么被折叠?



