当前位置: 首页 > article >正文

Golang学习笔记_31——原型模式

Golang学习笔记_28——工厂方法模式
Golang学习笔记_29——抽象工厂模式
Golang学习笔记_30——建造者模式


文章目录

    • 一、原型模式核心概念
      • 1. 定义
      • 2. 解决的问题
      • 3. 核心角色
      • 4. 类图
    • 二、原型模式的特点
    • 三、适用场景
      • 1. 对象创建成本高
      • 2. 需要动态配置对象
      • 3. 避免重复初始化
      • 4. 需要隔离对象构造细节
    • 四、与其他创建型模式的对比
    • 五、Go 语言代码示例
    • 六、原型模式的高级用法
      • 1. 原型注册表
      • 2. 动态配置对象
      • 3. 组合模式结合
    • 七、总结


一、原型模式核心概念

1. 定义

原型模式是一种 创建型设计模式,通过 复制现有对象(原型) 来创建新对象,而不是通过构造函数实例化。它适用于需要高效创建对象或动态配置对象的场景。

2. 解决的问题

  • 对象创建成本高:例如,对象初始化需要复杂计算、数据库查询或网络请求。
  • 动态配置对象:运行时需要基于现有对象的状态生成新对象。
  • 避免重复初始化:直接复制已有对象,跳过初始化步骤。

3. 核心角色

  1. Prototype(原型接口):声明克隆自身的方法(如 Clone())。
  2. ConcretePrototype(具体原型):实现 Clone() 方法的具体类。
  3. Client(客户端):通过调用 Clone() 方法生成新对象。

4. 类图

原型模式-类图

@startuml

' 定义接口和类
interface Prototype {
    + Clone(): Prototype
}

class Resume {
    - Name: string
    - Age: int
    - Skills: []string
    - Contact: map[string]string
    + Clone(): Prototype
    + Show(): void
}

' 实现关系
Resume .up.|> Prototype : implements

' 添加注释说明浅拷贝问题
note left of Resume::Clone
  **浅拷贝**:Skills切片和Contact映射
  与原对象共享底层数据!
end note

@enduml

二、原型模式的特点

优点

  1. 性能优化
    直接复制对象,避免重复执行耗时的初始化操作(如数据库连接)。

  2. 动态配置对象
    可以基于运行时状态克隆对象,并修改其属性。

  3. 避免构造约束
    绕过构造函数中的限制(例如私有构造函数或复杂参数)。

  4. 简化对象创建
    客户端无需知道对象的具体类型,只需调用 Clone()

缺点

  1. 深拷贝复杂性
    如果对象包含引用类型(如指针、切片、Map),需要手动实现深拷贝。

  2. 需要实现克隆方法
    每个需要复制的类必须实现 Clone() 方法,增加代码量。

  3. 可能破坏封装性
    某些语言中,克隆私有字段可能违反面向对象设计原则。

三、适用场景

1. 对象创建成本高

  • 示例:数据库连接对象、缓存对象、复杂配置对象。
  • 解决:首次创建后,后续通过克隆复用。

2. 需要动态配置对象

  • 示例:游戏中的敌人角色,基于现有角色生成变种(如修改颜色、攻击力)。
  • 解决:克隆基础对象,再修改特定属性。

3. 避免重复初始化

  • 示例:从模板生成文档(如合同、报告),模板初始化复杂。
  • 解决:克隆模板对象后填充数据。

4. 需要隔离对象构造细节

  • 示例:系统需要创建对象但无法直接访问其构造函数。
  • 解决:通过原型接口克隆对象。

四、与其他创建型模式的对比

模式核心目标关键区别
工厂方法通过子类创建对象通过继承实现,依赖具体类。
抽象工厂创建产品族关注对象家族,而非复制已有对象。
建造者分步骤构造复杂对象强调构造过程的控制,而非复制。
原型通过克隆生成对象直接复制现有对象,绕过构造函数。

五、Go 语言代码示例

场景描述
实现一个 简历生成系统,用户可以通过模板简历快速克隆并修改生成个性化简历。

代码实现

  1. 浅拷贝示例
package prototype_demo

import (
	"encoding/json"
	"fmt"
)

// 原型接口
type Prototype interface {
	Clone() Prototype
}

// 具体原型:简历
type Resume struct {
	Name    string
	Age     int
	Skills  []string
	Contact map[string]string
}

func (r *Resume) Clone() Prototype {
	// 浅拷贝
	return &Resume{
		Name:    r.Name,
		Age:     r.Age,
		Skills:  r.Skills,  // 浅拷贝切片 (共享底层数组)
		Contact: r.Contact, // 浅拷贝Map (共享底层哈希表)
	}
}

func (r *Resume) Display() {
	fmt.Printf("简历信息:\n"+
		"  姓名: %s\n"+
		"  年龄: %d\n"+
		"  技能: %v\n"+
		"  联系方式: %v\n",
		r.Name, r.Age, r.Skills, r.Contact)
}

// 深拷贝
func (r *Resume) DeepClone() Prototype {
	// 深拷贝
	data, _ := json.Marshal(r)
	var newResume Resume
	json.Unmarshal(data, &newResume)
	return &newResume
}

// 第二种深拷贝的方法
func (r *Resume) DeepClone2() Prototype {
	// 手动创建新对象并深度复制所有引用类型字段
	newResume := &Resume{
		Name: r.Name,
		Age:  r.Age,
	}

	// 深拷贝切片
	newResume.Skills = make([]string, len(r.Skills))
	copy(newResume.Skills, r.Skills)

	// 深拷贝Map
	newResume.Contact = make(map[string]string)
	for k, v := range r.Contact {
		newResume.Contact[k] = v
	}

	return newResume
}

func test() {
	// 原始简历模板
	templateResume := &Resume{
		Name:   "张三",
		Age:    25,
		Skills: []string{"Go", "Java"},
		Contact: map[string]string{
			"Email": "zhangsan@example.com",
			"Phone": "123456789",
		},
	}

	// 克隆简历并修改
	cloneResume := templateResume.Clone().(*Resume)
	cloneResume.Name = "李四"
	cloneResume.Skills = append(cloneResume.Skills, "Python") // 修改切片(会影响原对象!)
	cloneResume.Contact["Phone"] = "987654321"

	// 显示原始简历和克隆简历
	templateResume.Display()
	cloneResume.Display()
}

func test2() {
	// 原始简历模板
	templateResume := &Resume{
		Name:   "张三",
		Age:    25,
		Skills: []string{"Go", "Java"},
		Contact: map[string]string{
			"Email": "zhangsan@example.com",
			"Phone": "123456789",
		},
	}

	deepCloneResume := templateResume.DeepClone().(*Resume)
	deepCloneResume.Name = "李四"
	deepCloneResume.Skills = append(deepCloneResume.Skills, "Python") // 修改切片(不会影响原对象!)
	deepCloneResume.Contact["Phone"] = "987654321"
	deepCloneResume.Contact["Email"] = "lisi@example.com"

	templateResume.Display()
	deepCloneResume.Display()
}

func test3() {
	templateResume := &Resume{
		Name:   "张三",
		Age:    25,
		Skills: []string{"Go", "Java"},
		Contact: map[string]string{
			"Email": "zhangsan@example.com",
			"Phone": "123456789",
		},
	}

	deepCloneResume := templateResume.DeepClone2().(*Resume)
	deepCloneResume.Name = "李四"
	deepCloneResume.Skills = append(deepCloneResume.Skills, "Python") // 修改切片(不会影响原对象!)
	deepCloneResume.Contact["Phone"] = "987654321"
	deepCloneResume.Contact["Email"] = "lisi@example.com"

	templateResume.Display()
	deepCloneResume.Display()
}

输出结果

=== RUN   Test_test
-----------------------浅拷贝----------------------------
简历信息:
  姓名: 张三
  年龄: 25
  技能: [Go Java]
  联系方式: map[Email:zhangsan@example.com Phone:987654321]
简历信息:
  姓名: 李四
  年龄: 25
  技能: [Go Java Python]
  联系方式: map[Email:zhangsan@example.com Phone:987654321]
-----------------------深拷贝1----------------------------
简历信息:
  姓名: 张三
  年龄: 25
  技能: [Go Java]
  联系方式: map[Email:zhangsan@example.com Phone:123456789]
简历信息:
  姓名: 李四
  年龄: 25
  技能: [Go Java Python]
  联系方式: map[Email:lisi@example.com Phone:987654321]
-----------------------深拷贝2----------------------------
简历信息:
  姓名: 张三
  年龄: 25
  技能: [Go Java]
  联系方式: map[Email:zhangsan@example.com Phone:123456789]
简历信息:
  姓名: 李四
  年龄: 25
  技能: [Go Java Python]
  联系方式: map[Email:lisi@example.com Phone:987654321]
--- PASS: Test_test (0.00s)
PASS

六、原型模式的高级用法

1. 原型注册表

维护一个全局注册表,存储可复用的原型对象:

type PrototypeRegistry struct {
	prototypes map[string]Prototype
}

func (r *PrototypeRegistry) Get(key string) Prototype {
	return r.prototypes[key].Clone()
}

func (r *PrototypeRegistry) Set(key string, p Prototype) {
	r.prototypes[key] = p
}

2. 动态配置对象

克隆后动态修改对象属性:

// 克隆并修改技能
newResume := templateResume.Clone().(*Resume)
newResume.Skills = []string{"Go", "Rust"}

3. 组合模式结合

复杂对象的组件也可以通过原型模式复制:

type Project struct {
	Name    string
	Details *Resume  // 组合 Resume 对象
}

func (p *Project) Clone() Prototype {
	return &Project{
		Name:    p.Name,
		Details: p.Details.Clone().(*Resume),
	}
}

七、总结

原型模式通过 复制现有对象 的方式创建新对象,特别适合以下场景:

  1. 对象创建成本高:避免重复初始化。
  2. 需要动态配置对象:基于运行时状态生成变种。
  3. 隔离构造细节:绕过复杂构造函数。

在 Go 中实现时需注意 深拷贝问题,可通过序列化或手动复制引用类型字段解决。如果需要更复杂的深拷贝逻辑(如循环引用处理),可以结合其他技术(如反射、代码生成工具)实现。


http://www.kler.cn/a/550077.html

相关文章:

  • 【Java线程中断】线程中断后为什么要调用interrupt()?
  • XSS攻击(跨站脚本攻击)详解与实战
  • 智能选路+NAT实验
  • 分享一个解梦 Chrome 扩展 —— 周公 AI 解梦
  • 天翼云910B部署DeepSeek蒸馏70B LLaMA模型实践总结
  • Game Maker 0.11:《The Sandbox》创作愿景的全新篇章
  • 【学习】软件测试中的分类树法介绍
  • 设计模式3:代理、适配器、装饰器模式
  • 工厂设计模式一篇全部概括
  • 使用 Docker 部署 Elasticsearch:详细步骤与原创指南
  • 2025年人工智能十大趋势:AI如何塑造未来?
  • 有关表单autocomplete = “off“ 失效问题解决方案
  • Android MVC、MVP、MVVM、MVI的架构的区别
  • 武汉火影数字|VR大空间内容制作:开启沉浸式体验新时代
  • Python基础-使用dict和set
  • 走出养生误区,拥抱健康生活
  • 亿发解析2025年新零售趋势:全场景融合与深度数字化
  • nlp|微调大语言模型初探索(2),训练自己的聊天机器人
  • 如何设置爬虫的IP代理?
  • BSD协议栈:多播