缘起

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

原型模式

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

场景

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

设计

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

prototype_test.go

单元测试

  1. package patterns
  2. import (
  3. "fmt"
  4. pt "learning/gooop/creational_patterns/prototype"
  5. "testing"
  6. )
  7. func Test_Prototype(t *testing.T) {
  8. u1 := pt.DefaultUserFactory.Create()
  9. fmt.Printf("u1 = %v\n", u1)
  10. u2 := pt.DefaultUserFactory.Create()
  11. fmt.Printf("u2 = %v\n", u2)
  12. }

测试输出

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

ICloneable.go

定义克隆接口

  1. package prototype
  2. type ICloneable interface {
  3. Clone() ICloneable
  4. }

UserInfo.go

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

  1. package prototype
  2. type UserInfo struct {
  3. ID int
  4. Name string
  5. RoleList []string
  6. }
  7. func newEmptyUser() *UserInfo {
  8. return &UserInfo{}
  9. }
  10. func (me *UserInfo) Clone() ICloneable {
  11. roles := me.RoleList
  12. it := &UserInfo{
  13. me.ID, me.Name, make([]string, len(roles)),
  14. }
  15. for i,s := range roles {
  16. it.RoleList[i] = s
  17. }
  18. return it
  19. }

UserFactory.go

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

  1. package prototype
  2. import (
  3. "encoding/json"
  4. "strings"
  5. )
  6. // 用户工厂的全局单例
  7. var DefaultUserFactory IUserFactory = newUserFactory()
  8. type IUserFactory interface {
  9. Create() *UserInfo
  10. }
  11. type tUserFactory struct {
  12. defaultUserInfo *UserInfo
  13. }
  14. // 创建用户工厂实例
  15. func newUserFactory() *tUserFactory {
  16. reader := strings.NewReader(loadUserConfig())
  17. decoder := json.NewDecoder(reader)
  18. user := newEmptyUser()
  19. e := decoder.Decode(user)
  20. if e != nil {
  21. panic(e)
  22. }
  23. return &tUserFactory{
  24. defaultUserInfo: user,
  25. }
  26. }
  27. // 加载默认用户的属性配置
  28. func loadUserConfig() string {
  29. return `{
  30. "ID": 0,
  31. "Name" : "新用户",
  32. "RoleList" : ["guest"]
  33. }`
  34. }
  35. func (me *tUserFactory) Create() *UserInfo {
  36. return me.defaultUserInfo.Clone().(*UserInfo)
  37. }

原型模式小结

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