【设计模式】4、prototype 原型模式

文章介绍了在Go语言中如何使用Prototype模式实现inode、file和UserSetting对象的复制,包括接口实现、clone方法以及如何在UserSetting中进行深拷贝。通过实例展示了如何在目录结构和用户设置场景中应用这种设计模式。
摘要由CSDN通过智能技术生成


四、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,
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值