文章目录
- 
- 一、原型模式概述
- 
- [1. 什么是原型模式?](#1. 什么是原型模式?)
- [1.2 现实生活中的比喻](#1.2 现实生活中的比喻)
- [1.3 原型模式的优缺点](#1.3 原型模式的优缺点)
- [1.4 适用场景](#1.4 适用场景)
- [1.5 原型模式的UML图与核心角色](#1.5 原型模式的UML图与核心角色)
- [1.6 Go语言实现:浅拷贝与深拷贝](#1.6 Go语言实现:浅拷贝与深拷贝)
 
- 二、Go语言实现:一个完整的例子
 
一、原型模式概述
1. 什么是原型模式?
原型模式是一种创建型设计模式 ,它允许你通过复制(或克隆)一个现有的实例来创建新的实例,而不是通过 new 关键字和使用构造函数。
核心思想:创建对象的成本可能很高(例如,需要从数据库读取大量数据进行初始化),这时我们可以先创建一个原型对象,然后通过克隆这个原型来快速创建新的对象。
1.2 现实生活中的比喻
- 细胞分裂:一个细胞通过自我复制(克隆)产生一个完全相同的新细胞。新细胞继承了原细胞的所有特性,然后可以独立发展。
- 打印文件:你有一份复杂的电子文档(原型),你需要多份副本。你不会重新手动创建每一份,而是使用打印机的"复印"功能(克隆)来快速得到一模一样的副本。
1.3 原型模式的优缺点
优点:
- 提高性能:当创建对象的成本很高时,克隆比重新初始化要快得多。
- 简化对象创建:客户端代码不需要关心对象创建的复杂细节,只需调用一个克隆方法即可。
- 增加灵活性:可以在运行时动态地添加或删除要克隆的对象。
- 减少子类数量:如果创建对象需要复杂的工厂方法,使用原型模式可以避免创建庞大的工厂类层次结构。
缺点:
- 实现克隆可能很复杂:特别是当对象包含循环引用或对不支持深拷贝的资源(如文件句柄、网络连接)的引用时,实现深拷贝会非常困难。
- 需要为每个类实现克隆方法:这增加了代码的复杂性,并且必须处理深拷贝与浅拷贝的问题。
1.4 适用场景
- 创建对象成本高昂:例如,对象需要通过昂贵的数据库查询或RPC调用来初始化。
- 需要避免与对象创建相关的工厂类层次结构。
- 对象的实例状态只有少数几种组合:可以预先创建好这些组合的原型,然后按需克隆。
- 当客户端不需要知道对象创建的具体细节时。
1.5 原型模式的UML图与核心角色
原型模式的核心角色非常简单:
- 
Prototype(原型接口) :定义一个用于克隆自己的方法,通常是 Clone()。
- 
ConcretePrototype(具体原型) :实现 Prototype接口,并实现具体的克隆逻辑。
 UML 类图:+----------------+ +----------------------+ 
 | Client |------>| Prototype |
 +----------------+ +----------------------+
 | - prototype: Prototype | | + Clone() Prototype |
 | + Operation() | +----------------------+
 +----------------+ ^
 |
 | implements
 |
 +----------------------+
 | ConcretePrototype |
 +----------------------+
 | + field: type |
 | + Clone() Prototype |
 +----------------------+
1.6 Go语言实现:浅拷贝与深拷贝
在Go中实现原型模式,关键在于理解浅拷贝 和深拷贝的区别。
- 浅拷贝 :只复制对象本身和其包含的所有值类型 字段。对于引用类型(如切片、Map、指针、Channel),它只复制引用(地址),而不复制引用指向的数据。这意味着新对象和原型对象会共享同一份引用数据。
- 深拷贝 :不仅复制对象本身,还会递归地复制所有引用类型字段指向的数据。新对象和原型对象完全不共享任何数据,是完全独立的副本。
 Go语言中,可以通过&*操作符轻松实现浅拷贝,但深拷贝通常需要手动实现,或者使用序列化/反序列化的技巧(如json.Marshal/Unmarshal)。
二、Go语言实现:一个完整的例子
假设我们正在开发一个游戏,需要创建多种具有不同配置的敌人。创建一个敌人的成本很高(需要加载模型、贴图、AI配置等),所以我们使用原型模式。
第1步:定义原型接口
            
            
              go
              
              
            
          
          // Prototype: 定义一个可克隆的原型接口
type Prototype interface {
	Clone() Prototype
	GetInfo() string
}第2步:创建具体原型(包含浅拷贝和深拷贝的对比)
我们创建一个 Enemy 结构体,它包含一个值类型字段 Name 和一个引用类型字段 Weapons(一个切片)。
            
            
              go
              
              
            
          
          // ConcretePrototype: 敌人结构体
type Enemy struct {
	Name    string
	Health  int
	Weapons []string // 引用类型
}
// Clone 实现浅拷贝
func (e *Enemy) Clone() Prototype {
	// 浅拷贝:复制结构体,但切片 Weapons 只复制了引用(地址)
	fmt.Println("--> Performing Shallow Clone...")
	clone := *e // 复制结构体
	return &clone
}
// GetInfo 返回敌人信息
func (e *Enemy) GetInfo() string {
	return fmt.Sprintf("Enemy{Name: %s, Health: %d, Weapons: %v}", e.Name, e.Health, e.Weapons)
}
// CloneDeep 实现深拷贝
func (e *Enemy) CloneDeep() Prototype {
	fmt.Println("--> Performing Deep Clone...")
	// 深拷贝:需要手动复制所有引用类型字段
	clone := &Enemy{
		Name:    e.Name,
		Health:  e.Health,
		Weapons: make([]string, len(e.Weapons)), // 创建一个新的切片
	}
	// 将原切片的元素复制到新切片中
	copy(clone.Weapons, e.Weapons)
	return clone
}第3步:客户端使用
            
            
              go
              
              
            
          
          func main() {
	fmt.Println("--- Prototype Pattern in Go ---")
	// 1. 创建一个原型敌人
	originalEnemy := &Enemy{
		Name:    "Goblin Archer",
		Health:  50,
		Weapons: []string{"Short Bow", "Dagger"},
	}
	fmt.Printf("Original Enemy: %s (Weapons Address: %p)\n", originalEnemy.GetInfo(), originalEnemy.Weapons)
	fmt.Println("\n--- Testing Shallow Clone ---")
	// 2. 克隆一个新敌人(浅拷贝)
	shallowClone := originalEnemy.Clone().(*Enemy)
	fmt.Printf("Shallow Clone: %s (Weapons Address: %p)\n", shallowClone.GetInfo(), shallowClone.Weapons)
	// 3. 修改克隆对象的引用类型字段
	fmt.Println("\nModifying shallow clone's weapon...")
	shallowClone.Weapons[0] = "Long Bow"
	// 4. 检查原对象是否受影响
	fmt.Printf("After modification:\n")
	fmt.Printf("Original Enemy: %s\n", originalEnemy.GetInfo()) // 原对象的武器也被修改了!
	fmt.Printf("Shallow Clone: %s\n", shallowClone.GetInfo())
	fmt.Println("\n========================================\n")
	fmt.Println("--- Testing Deep Clone ---")
	// 5. 克隆一个新敌人(深拷贝)
	deepClone := originalEnemy.CloneDeep().(*Enemy)
	fmt.Printf("Deep Clone: %s (Weapons Address: %p)\n", deepClone.GetInfo(), deepClone.Weapons)
	// 6. 修改深拷贝对象的引用类型字段
	fmt.Println("\nModifying deep clone's weapon...")
	deepClone.Weapons[0] = "Crossbow"
	// 7. 检查原对象是否受影响
	fmt.Printf("After modification:\n")
	fmt.Printf("Original Enemy: %s\n", originalEnemy.GetInfo()) // 原对象不受影响
	fmt.Printf("Deep Clone: %s\n", deepClone.GetInfo())
}完整代码(可直接运行)
            
            
              go
              
              
            
          
          package main
import "fmt"
// ======================
// 1. 定义原型接口
// ======================
// Prototype: 定义一个可克隆的原型接口
type Prototype interface {
	Clone() Prototype
	GetInfo() string
}
// ======================
// 2. 创建具体原型
// ======================
// ConcretePrototype: 敌人结构体
type Enemy struct {
	Name    string
	Health  int
	Weapons []string // 引用类型,用于演示深浅拷贝的区别
}
// Clone 实现浅拷贝
func (e *Enemy) Clone() Prototype {
	// 浅拷贝:复制结构体,但切片 Weapons 只复制了引用(地址)
	fmt.Println("--> Performing Shallow Clone...")
	clone := *e // 复制结构体
	return &clone
}
// GetInfo 返回敌人信息
func (e *Enemy) GetInfo() string {
	return fmt.Sprintf("Enemy{Name: %s, Health: %d, Weapons: %v}", e.Name, e.Health, e.Weapons)
}
// CloneDeep 实现深拷贝
func (e *Enemy) CloneDeep() Prototype {
	fmt.Println("--> Performing Deep Clone...")
	// 深拷贝:需要手动复制所有引用类型字段
	clone := &Enemy{
		Name:    e.Name,
		Health:  e.Health,
		Weapons: make([]string, len(e.Weapons)), // 创建一个新的切片
	}
	// 将原切片的元素复制到新切片中
	copy(clone.Weapons, e.Weapons)
	return clone
}
// ======================
// 3. 客户端使用
// ======================
func main() {
	fmt.Println("--- Prototype Pattern in Go ---")
	// 1. 创建一个原型敌人
	originalEnemy := &Enemy{
		Name:    "Goblin Archer",
		Health:  50,
		Weapons: []string{"Short Bow", "Dagger"},
	}
	fmt.Printf("Original Enemy: %s (Weapons Address: %p)\n", originalEnemy.GetInfo(), originalEnemy.Weapons)
	fmt.Println("\n--- Testing Shallow Clone ---")
	// 2. 克隆一个新敌人(浅拷贝)
	shallowClone := originalEnemy.Clone().(*Enemy)
	fmt.Printf("Shallow Clone: %s (Weapons Address: %p)\n", shallowClone.GetInfo(), shallowClone.Weapons)
	// 3. 修改克隆对象的引用类型字段
	fmt.Println("\nModifying shallow clone's weapon...")
	shallowClone.Weapons[0] = "Long Bow"
	// 4. 检查原对象是否受影响
	fmt.Printf("After modification:\n")
	fmt.Printf("Original Enemy: %s\n", originalEnemy.GetInfo()) // 原对象的武器也被修改了!
	fmt.Printf("Shallow Clone: %s\n", shallowClone.GetInfo())
	fmt.Println("\n========================================\n")
	fmt.Println("--- Testing Deep Clone ---")
	// 5. 克隆一个新敌人(深拷贝)
	deepClone := originalEnemy.CloneDeep().(*Enemy)
	fmt.Printf("Deep Clone: %s (Weapons Address: %p)\n", deepClone.GetInfo(), deepClone.Weapons)
	// 6. 修改深拷贝对象的引用类型字段
	fmt.Println("\nModifying deep clone's weapon...")
	deepClone.Weapons[0] = "Crossbow"
	// 7. 检查原对象是否受影响
	fmt.Printf("After modification:\n")
	fmt.Printf("Original Enemy: %s\n", originalEnemy.GetInfo()) // 原对象不受影响
	fmt.Printf("Deep Clone: %s\n", deepClone.GetInfo())
}执行结果
            
            
              bash
              
              
            
          
          --- Prototype Pattern in Go ---
Original Enemy: Enemy{Name: Goblin Archer, Health: 50, Weapons: [Short Bow Dagger]} (Weapons Address: 0x1400012c030)
--- Testing Shallow Clone ---
--> Performing Shallow Clone...
Shallow Clone: Enemy{Name: Goblin Archer, Health: 50, Weapons: [Short Bow Dagger]} (Weapons Address: 0x1400012c030)
Modifying shallow clone's weapon...
After modification:
Original Enemy: Enemy{Name: Goblin Archer, Health: 50, Weapons: [Long Bow Dagger]}
Shallow Clone: Enemy{Name: Goblin Archer, Health: 50, Weapons: [Long Bow Dagger]}
========================================
--- Testing Deep Clone ---
--> Performing Deep Clone...
Deep Clone: Enemy{Name: Goblin Archer, Health: 50, Weapons: [Short Bow Dagger]} (Weapons Address: 0x1400012c060)
Modifying deep clone's weapon...
After modification:
Original Enemy: Enemy{Name: Goblin Archer, Health: 50, Weapons: [Long Bow Dagger]}
Deep Clone: Enemy{Name: Goblin Archer, Health: 50, Weapons: [Crossbow Dagger]}从结果中可以清晰地看到:
- 浅拷贝 :originalEnemy和shallowClone的Weapons字段指向同一个内存地址。修改克隆体的武器,原体的武器也跟着变了。
- 深拷贝 :originalEnemy和deepClone的Weapons字段指向不同的内存地址。修改克隆体的武器,原体完全不受影响。
总结:原型模式在Go中是一个非常实用的模式,尤其是在需要高效创建复杂对象的场景。
- 核心是 Clone()方法 :为你的结构体实现一个Clone()方法。
- 关键在于拷贝方式 :你必须根据业务需求,决定是使用浅拷贝 还是深拷贝 。
- 如果对象只包含值类型,或者共享引用数据没有问题,浅拷贝简单高效。
- 如果对象包含引用类型,并且新对象需要独立修改,那么必须实现深拷贝。
 
- Go的实现 :Go没有内置的 Cloneable接口或clone()方法,这使得原型模式的实现更加灵活,但也要求开发者自己处理拷贝的细节。