头图

手撸golang 创建型设计模式 原型模式

ioly

手撸golang 创建型设计模式 原型模式

缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用golang练习之

原型模式

原型模式(Prototype Pattern)指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,属于创建型设计模式。
_

场景

  • 某多用户业务系统, 提供用户自助注册的功能
  • 用户具有ID, Name, RolList(角色列表)等属性
  • 创建用户时, 需要给用户默认分配"guest" 角色
  • 用户的默认值, 使用json文件进行配置

设计

  • 定义UserInfo类, 表示用户信息
  • UserInfo类实现了ICloneable接口, 可以深度克隆自身
  • 定义UserFactory类, 作为创建新用户的简单工厂
  • 初始化UserFactory时, 从磁盘加载默认用户的属性配置, 并创建一个UserInfo实例, 作为用户信息的原型
  • 创建新用户时, 调用原型用户的Clone()方法, 复制副本并返回

prototype_test.go

单元测试

package patterns

import (
    "fmt"
    pt "learning/gooop/creational_patterns/prototype"
    "testing"
)

func Test_Prototype(t *testing.T) {
    u1 := pt.DefaultUserFactory.Create()
    fmt.Printf("u1 = %v\n", u1)

    u2 := pt.DefaultUserFactory.Create()
    fmt.Printf("u2 = %v\n", u2)
}

测试输出

$ go test -v prototype_test.go 
=== RUN   Test_Prototype
u1 = &{0 新用户 [guest]}
u2 = &{0 新用户 [guest]}
--- PASS: Test_Prototype (0.00s)
PASS
ok      command-line-arguments  0.002s

ICloneable.go

定义克隆接口

package prototype

type ICloneable interface {
    Clone() ICloneable
}

UserInfo.go

UserInfo封装了用户信息, 并实现了ICloneable接口, 可以深度克隆自身

package prototype

type UserInfo struct {
    ID int
    Name string
    RoleList []string
}

func newEmptyUser() *UserInfo {
    return &UserInfo{}
}

func (me *UserInfo) Clone() ICloneable {
    roles := me.RoleList
    it := &UserInfo{
        me.ID, me.Name, make([]string, len(roles)),
    }

    for i,s := range roles {
        it.RoleList[i] = s
    }
    return it
}

UserFactory.go

UserFactory实现了创建UserInfo的简单工厂.
创建的过程本质是调用了用户原型的Clone方法.
用户原型是从json配置加载的, 便于按需修改配置.

package prototype

import (
    "encoding/json"
    "strings"
)

// 用户工厂的全局单例
var DefaultUserFactory IUserFactory = newUserFactory()

type IUserFactory interface {
    Create() *UserInfo
}

type tUserFactory struct {
    defaultUserInfo *UserInfo
}

// 创建用户工厂实例
func newUserFactory() *tUserFactory {
    reader := strings.NewReader(loadUserConfig())
    decoder := json.NewDecoder(reader)
    user := newEmptyUser()
    e := decoder.Decode(user)
    if e != nil {
        panic(e)
    }

    return &tUserFactory{
        defaultUserInfo: user,
    }
}

// 加载默认用户的属性配置
func loadUserConfig() string {
    return `{
    "ID": 0,
    "Name" : "新用户",
    "RoleList" : ["guest"]
}`
}


func (me *tUserFactory) Create() *UserInfo {
    return me.defaultUserInfo.Clone().(*UserInfo)
}

原型模式小结

原型模式的优点
(1) 某些时候克隆比直接new一个对象再逐属性赋值的过程更简洁高效。
(2)可以使用深克隆方式保存对象的状态,可辅助实现撤销操作。
原型模式的缺点
(1)需要配置一个clone方法。
(2)clone方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
(3)当实现深克隆时,需要编写较为复杂的代码,尤其当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆。因此,深克隆、浅克隆需要运用得当。

(end)

阅读 678

想当将军 首先要肯打

42 声望
17 粉丝
0 条评论

想当将军 首先要肯打

42 声望
17 粉丝
文章目录
宣传栏