原型模式
简介
原型模式(Prototype Pattern)主要思想是通过复制现有对象来创建新对象,而不是新建实例。这样可以避免重复初始化对象的开销,特别是当对象创建成本较高时。
UML
nil
角色组成
- 原型:声明
Clone()
方法及其他方法的接口 - 具体原型
- 客户端
适用场景
- 如果开发者需要复制一些对象,并且希望代码独立于这些对象所属的具体类,则可以使用原型模式。
- 当开发者需要将应用程序中类的数量保持在最少时,如果子类之间的区别仅在于其对象的初始化方式,则可以使用原型模式减少子类的数量。
- 如果需要在运行时实例化类,并且客户端无须根据需求对子类进行实例化,只需找到合适的原型并对其进行复制,则可以使用原型模式。
- 原型模式为客户端提供了一个通用接口,客户端可以通过这个接口与所有克隆对象进行交互,并且使客户端与其复制对象的具体类相互独立。
优点
-
性能优化 通过复制现有对象避免重复执行耗时的初始化操作(如数据库查询、复杂计算),尤其适合创建成本高的对象。
-
简化对象创建 隐藏对象创建的复杂性,客户端无需知道对象的具体类型,只需调用克隆方法即可生成新对象。
-
动态扩展对象 支持运行时动态添加或修改对象的行为,通过克隆生成新对象后,可灵活调整其属性或状态。
-
避免构造约束 绕过构造函数直接复制对象,适用于构造函数存在限制(如私有化)或需要绕过初始化流程的场景。
缺点
-
深拷贝与浅拷贝问题 需明确实现对象的深拷贝逻辑,否则可能因引用共享导致数据意外修改(如对象包含其他对象或集合时)。
-
实现复杂性 每个需克隆的类必须实现克隆接口(如
Cloneable
),对包含循环引用的对象需额外处理,增加代码维护成本。 -
破坏封装性 克隆方法可能直接访问对象私有字段,破坏面向对象的封装原则(某些语言需通过反射实现)。
-
继承关系处理复杂 若父类已实现克隆方法,子类需显式调用
super.clone()
并处理自身字段,易因遗漏导致错误。
示例代码
Go
package prototypepattern
import "fmt"
// 原型接口
type InterfaceNode interface {
Print(string) string
Clone() InterfaceNode
}
// 具体原型: 文件类
type File struct {
Name string
}
func (f *File) Print(indentation string) string {
fmt.Println(indentation + f.Name)
return indentation + f.Name
}
func (f *File) Clone() InterfaceNode {
return &File{Name: f.Name + "_Clone"}
}
// 具体原型: 文件夹类
type Folder struct {
Children []InterfaceNode
Name string
}
func (f *Folder) Print(indentation string) string {
fmt.Println(indentation + f.Name)
for _, i := range f.Children {
i.Print(indentation + indentation)
}
return indentation + f.Name
}
func (f *Folder) Clone() InterfaceNode {
CloneFolder := &Folder{Name: f.Name + "_Clone"}
var tmpChildren []InterfaceNode
for _,i := range f.Children {
copy := i.Clone()
tmpChildren = append(tmpChildren, copy)
}
CloneFolder.Children = tmpChildren
return CloneFolder
}
client
package prototypepattern
import (
"testing"
)
func TestPrototype(t *testing.T) {
File1 := &File{Name: "file1"}
File2 := &File{Name: "file2"}
File3 := &File{Name: "file3"}
Folder1 := &Folder{
Children: []InterfaceNode{File1},
Name: "Folder1",
}
Folder2 := &Folder{
Children: []InterfaceNode{Folder1, File2, File3},
Name: "Folder2",
}
Folder2.Print(" ")
t.Logf("\n打印复制文件夹 Folder2 的层级\n")
CloneFolder := Folder2.Clone()
CloneFolder.Print(" ")
}
Python
import copy
from abc import ABC, abstractmethod
class Node(ABC):
@abstractmethod
def print(self, indent: str) -> str:
pass
@abstractmethod
def clone(self):
pass
class File(Node):
def __init__(self, name: str):
self.name = name
def print(self, indent: str) -> str:
print(f"{indent}{self.name}")
return f"{indent}{self.name}"
def clone(self):
# 浅拷贝足够(字符串不可变)
return File(self.name + "_Clone")
class Folder(Node):
def __init__(self, name: str):
self.name = name
self.children = []
def add(self, node: Node):
self.children.append(node)
def print(self, indent: str) -> str:
print(f"{indent}{self.name}")
for child in self.children:
child.print(indent + "\t")
return f"{indent}{self.name}"
def clone(self):
# 深拷贝关键点:递归克隆子元素
clone_folder = Folder(self.name + "_Clone")
clone_folder.children = [child.clone() for child in self.children]
return clone_folder
# 测试使用
if __name__ == "__main__":
# 创建原型结构
original = Folder("Documents")
original.add(File("resume.pdf"))
sub_folder = Folder("Projects")
sub_folder.add(File("main.py"))
original.add(sub_folder)
# 执行克隆
cloned = original.clone()
# 修改克隆体不影响原型
cloned.name = "Cloned_Documents"
cloned.children[1].children[0].name = "modified.py"
print("\nOriginal structure:")
original.print("")
print("\nCloned structure:")
cloned.print("")