跳到主要内容

原型模式

简介

原型模式(Prototype Pattern)主要思想是通过复制现有对象来创建新对象,而不是新建实例。这样可以避免重复初始化对象的开销,特别是当对象创建成本较高时。

UML

nil

角色组成

  • 原型:声明 Clone()方法及其他方法的接口
  • 具体原型
  • 客户端

适用场景

  • 如果开发者需要复制一些对象,并且希望代码独立于这些对象所属的具体类,则可以使用原型模式。
  • 当开发者需要将应用程序中类的数量保持在最少时,如果子类之间的区别仅在于其对象的初始化方式,则可以使用原型模式减少子类的数量。
  • 如果需要在运行时实例化类,并且客户端无须根据需求对子类进行实例化,只需找到合适的原型并对其进行复制,则可以使用原型模式。
  • 原型模式为客户端提供了一个通用接口,客户端可以通过这个接口与所有克隆对象进行交互,并且使客户端与其复制对象的具体类相互独立。

优点

  1. 性能优化 通过复制现有对象避免重复执行耗时的初始化操作(如数据库查询、复杂计算),尤其适合创建成本高的对象。

  2. 简化对象创建 隐藏对象创建的复杂性,客户端无需知道对象的具体类型,只需调用克隆方法即可生成新对象。

  3. 动态扩展对象 支持运行时动态添加或修改对象的行为,通过克隆生成新对象后,可灵活调整其属性或状态。

  4. 避免构造约束 绕过构造函数直接复制对象,适用于构造函数存在限制(如私有化)或需要绕过初始化流程的场景。

缺点

  1. 深拷贝与浅拷贝问题 需明确实现对象的深拷贝逻辑,否则可能因引用共享导致数据意外修改(如对象包含其他对象或集合时)。

  2. 实现复杂性 每个需克隆的类必须实现克隆接口(如 Cloneable),对包含循环引用的对象需额外处理,增加代码维护成本。

  3. 破坏封装性 克隆方法可能直接访问对象私有字段,破坏面向对象的封装原则(某些语言需通过反射实现)。

  4. 继承关系处理复杂 若父类已实现克隆方法,子类需显式调用 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("")