当前位置:首页>文章>设计模式>原型模式详解 | 深入理解 Prototype Pattern 的实现与应用

原型模式详解 | 深入理解 Prototype Pattern 的实现与应用

原型模式详解

1️⃣ 概念

定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

特点: 不需要知道任何的创建细节,并且不调用构造函数

类型: 创建型

2️⃣ 适用场景

  • 类初始化消耗太多资源
  • 直接创建一个对象需要非常繁琐的过程(数据准备、访问权限等)
  • 构造函数比较复杂
  • 循环体中生产大量的对象时

3️⃣ 优点

  • 原型模式性能比直接创建一个对象性能高
  • 简化创建过程

4️⃣ 缺点

  • 必须配备克隆方法
  • 对克隆复杂对象或者对克隆出的对象进行复杂改造时,容易引入风险
  • 深拷贝和浅拷贝要运用得当

5️⃣ 原型模式 Coding

① 创建一个发邮件的场景

import copy

class Mail:
    def __init__(self):
        self.name = None
        self.email_address = None
        self.content = None
        print("Mail Class Constructor")

    def get_name(self):
        return self.name

    def set_name(self, name):
        self.name = name

    def get_email_address(self):
        return self.email_address

    def set_email_address(self, email_address):
        self.email_address = email_address

    def get_content(self):
        return self.content

    def set_content(self, content):
        self.content = content

    def __str__(self):
        return f"Mail{{name='{self.name}', email_address='{self.email_address}', content='{self.content}'}}{id(self)}"

    def clone(self):
        print("clone mail object")
        return copy.copy(self)

② 创建 MailUtil 工具类

class MailUtil:
    @staticmethod
    def send_mail(mail):
        output_content = "向{0}同学,邮件地址:{1},邮件内容:{2}发送邮件成功"
        print(output_content.format(mail.get_name(), mail.get_email_address(), mail.get_content()))

    @staticmethod
    def save_origin_mail_record(mail):
        print(f"存储originMail记录,originMail:{mail.get_content()}")

③ 编写测试类

def test():
    mail = Mail()
    mail.set_content("初始化模板")
    print(f"初始化mail:{mail}")

    for i in range(10):
        mail_temp = mail.clone()
        mail_temp.set_name(f"姓名{i}")
        mail_temp.set_email_address(f"姓名{i}@test.com")
        mail_temp.set_content("恭喜您,此次活动中奖了")
        MailUtil.send_mail(mail_temp)
        print(f"克隆的mail_temp:{mail_temp}")

    MailUtil.save_origin_mail_record(mail)

if __name__ == "__main__":
    test()

6️⃣ UML类图

原型模式详解 | 深入理解 Prototype Pattern 的实现与应用

7️⃣ 深克隆与浅克隆

(1)浅克隆

① 创建 Pig 类

import copy
from datetime import datetime

class Pig:
    def __init__(self, name, birthday):
        self.name = name
        self.birthday = birthday

    def get_name(self):
        return self.name

    def set_name(self, name):
        self.name = name

    def get_birthday(self):
        return self.birthday

    def set_birthday(self, birthday):
        self.birthday = birthday

    def clone(self):
        return copy.copy(self)

    def __str__(self):
        return f"Pig{{name='{self.name}', birthday={self.birthday}}}{id(self)}"

② 编写测试类

def test():
    birthday = datetime.fromtimestamp(0)
    pig1 = Pig("佩奇", birthday)
    pig2 = pig1.clone()
    print(pig1)
    print(pig2)

    pig1.get_birthday().replace(year=2021, month=2, day=14)
    # 注意:datetime是不可变对象,需要重新赋值
    pig1.birthday = datetime.fromtimestamp(666666666)

    print(pig1)
    print(pig2)

if __name__ == "__main__":
    test()

分析结果:

从结果中可以看出,先打印的pig1与pig2的内容是一致的,在设置完生日以后打印的一组结果内容也是一致的,但是我们发现在我们设置完pig1的生日以后,后打印的一组内容的生日都发生了变化,这又是为什么呢?我们通过debug找到了答案:

通过分析我们找到了原因,pig1与pig2对象所使用的生日属性都是一个对象的,所以我们在修改完生日以后两个对象的内容都发生了变化(也就是说我们引用的克隆对象都是同一个对象,当我们修改被克隆对象的属性的时候,克隆出来的对象属性也会跟着发生变化);这就是浅克隆,同时默认的也是浅克隆。

(2)深克隆

从上边的结论中我们可以得出,默认的克隆方式是浅克隆,那么怎么实现深克隆呢?其实也简单,只要我们对对象的引用类型也添加克隆实现就可以解决了:

def clone(self):
    # 深克隆
    return copy.deepcopy(self)

通过使用copy.deepcopy()方法我们可以看出在我们进行了引用属性的深克隆以后,最后一组对象的内容已经发生了变化,同时debug的时候内存中引用的对象也发生了变化,效果已经达到了我们的预期。

注意: 在使用原型模式的时候一定要进行深克隆,否则可能会出现Bug

8️⃣ 原型模式破坏单例

① 使用 HungrySingleton

import copy

class HungrySingleton:
    _instance = None
    _initialized = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if HungrySingleton._initialized:
            raise RuntimeError("单例构造器禁止重复调用")
        HungrySingleton._initialized = True

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    def clone(self):
        return copy.copy(self)

② 编写测试类

def test():
    hungry_singleton = HungrySingleton.get_instance()
    clone_hungry_singleton = hungry_singleton.clone()
    print(hungry_singleton)
    print(clone_hungry_singleton)
    print(f"是否为同一对象: {hungry_singleton is clone_hungry_singleton}")

if __name__ == "__main__":
    test()

可以看到通过使用clone我们依旧破坏了单例,那要如何才能解决这个问题呢?

解决方案

① 单例类不实现clone方法

② 单例类在重写clone方法时不使用默认的实现,将其修改为:

def clone(self):
    return self.get_instance()

可以看到通过以上的修改,再次运行测试类的时候两个对象就完全是一致的了。

9️⃣ 原型模式的实际应用

① Python标准库中原型模式的使用

list的copy方法实现:

# Python中的list支持浅拷贝
original_list = [1, 2, [3, 4]]
shallow_copy = original_list.copy()  # 或者使用 original_list[:]
deep_copy = copy.deepcopy(original_list)

print(f"原始列表: {original_list}")
print(f"浅拷贝: {shallow_copy}")
print(f"深拷贝: {deep_copy}")

# 修改嵌套列表
original_list[2].append(5)
print(f"修改后原始列表: {original_list}")
print(f"修改后浅拷贝: {shallow_copy}")  # 会受到影响
print(f"修改后深拷贝: {deep_copy}")    # 不会受到影响

dict的copy方法实现:

import copy

# Python中的dict也支持拷贝
original_dict = {'a': 1, 'b': {'c': 2}}
shallow_copy = original_dict.copy()
deep_copy = copy.deepcopy(original_dict)

print(f"原始字典: {original_dict}")
print(f"浅拷贝: {shallow_copy}")
print(f"深拷贝: {deep_copy}")

# 修改嵌套字典
original_dict['b']['c'] = 3
print(f"修改后原始字典: {original_dict}")
print(f"修改后浅拷贝: {shallow_copy}")  # 会受到影响
print(f"修改后深拷贝: {deep_copy}")    # 不会受到影响

同理Python标准库中的很多容器类型都实现了copy方法,也就是说它们也应用了原型模式。

设计模式

Python单例模式详解:概念、优缺点与实现方式

2025-9-3 10:56:14

设计模式

外观模式详解|设计模式学习指南

2025-9-5 10:50:12

搜索