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

设计模式学习手册(四)(原型模式)

写在前面

  • 书接上文设计模式学习手册(三)(建造者模式)
  • 原型模式简单来说就是复制一个已存在的原型实例,并对其进行必要的修改,来创建新的对象。原型模式通常会有一个clone()方法用于复制对象。
  • 优点:
    • 直接复制现有对象,避免了重复的初始化过程,减少开销。
    • 可以动态地改变克隆对象的属性,适应不同的需求。
    • 无需关心对象的构造细节,通过复制现有实例即可创建新对象。
  • 缺点:
    • 会涉及到编程中的一个经典问题:深浅拷贝
    • Clone方法位于类的内部,改造的时候需要修改代码,违背了开闭原则;
    • 并不是所有对象都适合使用原型模式,尤其是那些具有复杂依赖关系或资源限制的对象

原型模式的简单尝试

  • 先尝试有一种传统的写法,需要不断的创建新的实例,当构造函数涉及到的属性很多后,这种方式就显得效率低下了
class person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def todict(self):
        return {"name": self.name, "age": self.age}


if __name__ == '__main__':
    a = person("John", 25)
    b = person("John", 25)
    c = person("John", 25)

    print(a.todict())
    print(b.todict())
    print(c.todict())
  • 还有一种写法,直接把a引用所指向的地址赋值给b和c,但要注意,当你修改a的时候,b和c也会同时变化
if __name__ == '__main__':
    a = person("John", 25)
    b = a
    c = a

    a.name = "Jane"

    print(a.todict())
    print(b.todict())
    print(c.todict())
  • 使用原型模式
import copy


# 原型接口
class Prototype:
    def clone(self):
        pass


# 具体原型类
class ConcretePrototype(Prototype):
    def __init__(self, name, interests):
        self.name = name
        self.interests = interests  

    def get_name(self):
        return self.name

    def get_interests(self):
        return self.interests

    def clone(self):
        # 使用浅拷贝
        return copy.copy(self)


# 客户端代码
if __name__ == "__main__":
    prototype1 = ConcretePrototype("jack", ['classical music', 'rock music'])
    prototype2 = prototype1.clone()

    print(f"Prototype 1: {prototype1.get_name()}, Items: {prototype1.get_interests()}")
    print(f"Prototype 2: {prototype2.get_name()}, Items: {prototype2.get_interests()}")
  • 原型模式一般有以下2个组成
    • 原型接口:定义一个接口,要求实现该接口的类必须提供一个克隆自身的功能。通常,clone()方法用于复制对象。
    • 具体原型类:实现原型接口的类,负责实现具体的克隆逻辑。

原型模式之深拷贝

浅拷贝

在上面的示例代码中,尝试下修改属性

if __name__ == "__main__":
    prototype1 = ConcretePrototype("jack", ['classical music', 'rock music'])
    prototype2 = prototype1.clone()

    # 修改 prototype2 的 interests 属性
    prototype2.get_interests().append('jazz music')
    prototype1.name = "jane"

    print(f"Prototype 1: {prototype1.get_name()}, Items: {prototype1.get_interests()}")
    print(f"Prototype 2: {prototype2.get_name()}, Items: {prototype2.get_interests()}")
    
# Output:
# Prototype 1: jane, Items: ['classical music', 'rock music', 'jazz music']
# Prototype 2: jack, Items: ['classical music', 'rock music', 'jazz music']

不难发现修改name属性,不会相互影响,但是修改interests属性,prototype1prototype2都受到了影响。主要原因是:浅拷贝中的元素,只拷贝了表面的一层,因此,如果原对象中包含了引用对象,改变其也会影响拷贝后的对象

深拷贝

  • 只要修改clone函数即可
  • 深度拷贝则会递归地拷贝原对象中的每一个子对象,因此拷贝后的对象和原对象互不相关
import copy


# 原型接口
class Prototype:
    def clone(self):
        pass


# 具体原型类
class ConcretePrototype(Prototype):
    def __init__(self, name, interests):
        self.name = name
        self.interests = interests  

    def get_name(self):
        return self.name

    def get_interests(self):
        return self.interests

    def clone(self):
        # 使用浅拷贝
        return copy.deepcopy(self)


# 客户端代码
if __name__ == "__main__":
    prototype1 = ConcretePrototype("jack", ['classical music', 'rock music'])
    prototype2 = prototype1.clone()

    # 修改 prototype2 的 interests 属性
    prototype2.get_interests().append('jazz music')
    prototype1.name = "jane"

    print(f"Prototype 1: {prototype1.get_name()}, Items: {prototype1.get_interests()}")
    print(f"Prototype 2: {prototype2.get_name()}, Items: {prototype2.get_interests()}")

# Output:
# Prototype 1: jane, Items: ['classical music', 'rock music']
# Prototype 2: jack, Items: ['classical music', 'rock music', 'jazz music']

原型模式+工厂模式

  • 原型模式实际项目中,使用的场景并不多,但原型模式常常和其他的设计模式混搭着使用,这里就简单写了个demo
import copy


# 原型接口
class Prototype:
    def clone(self):
        pass


# 具体原型类
class ConcretePrototype(Prototype):
    def __init__(self, name, interests):
        self.name = name
        self.interests = interests

    def get_name(self):
        return self.name

    def get_interests(self):
        return self.interests

    def clone(self):
        # 返回一个克隆对象
        return copy.deepcopy(self)


# 工厂类,用于生产对象
class PrototypeFactory:
    def __init__(self):
        # 初始化时可以设置多个原型对象
        self.prototypes = {}

    def register_prototype(self, name, prototype):
        """ 注册原型对象 """
        self.prototypes[name] = prototype

    def create_prototype(self, name):
        """ 根据名字克隆原型对象 """
        prototype = self.prototypes.get(name)
        if prototype:
            return prototype.clone()
        else:
            print(f"Prototype {name} not found!")
            return None


# 客户端代码
if __name__ == "__main__":
    # 创建原型对象
    prototype1 = ConcretePrototype("jack", ['classical music', 'rock music'])
    prototype2 = ConcretePrototype("alice", ['jazz music', 'pop music'])

    # 创建工厂对象并注册原型
    factory = PrototypeFactory()
    factory.register_prototype("jackPrototype", prototype1)
    factory.register_prototype("alicePrototype", prototype2)

    # 通过工厂创建新的对象
    cloned_object1 = factory.create_prototype("jackPrototype")
    cloned_object2 = factory.create_prototype("alicePrototype")

    # 修改克隆对象的属性
    cloned_object1.get_interests().append("hip hop")
    cloned_object2.get_name()  # "alice"

    # 输出克隆对象的属性
    print(f"Cloned Object 1: {cloned_object1.get_name()}, Interests: {cloned_object1.get_interests()}")
    print(f"Cloned Object 2: {cloned_object2.get_name()}, Interests: {cloned_object2.get_interests()}")

  • 不难发现这里面多了一个PrototypeFactory(工厂类),用于管理多个原型对象,可以注册原型并通过原型创建新对象。工厂使用 clone() 方法来创建对象实例,而不直接使用构造函数

最后

  • 原型模式理解起来应该不算复杂,核心就是拷贝
  • 原型模式往往用于类初始化非常复杂的场景,或者一个对象要被多个修改者操作的场景
  • 原型模式很少单独使用,学会将原型模式结合其他的设计模式一起使用。在项目中要多思考多尝试。

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

相关文章:

  • C++—17、C++ 中的类和结构体的区别
  • 《计算机网络》课后探研题书面报告_了解PPPoE协议
  • 基于Springboot: 宠物小程序开发笔记(上)
  • Unity2017 控制.abc格式的三维动画播放
  • Kylin Linux V10 替换安装源,并在服务器上启用 EPEL 仓库
  • Python 替换excel 单元格内容
  • Pyinstaller打包
  • Python与Excel:开启自动化办公新时代
  • AudioGPT全新的 音频内容理解与生成系统
  • 深入理解 MySQL 中 FIND_IN_SET 函数在查询中的应用
  • 美化IDE之修改IDEA启动界面logo图片
  • laravel中请求失败重试的扩展--Guzzle
  • 【Hive】海量数据存储利器之Hive库原理初探
  • mysql,PostgreSQL,Oracle数据库sql的细微差异(2) [whith as; group by; 日期引用]
  • 24-25-1-单片机开卷部分习题和评分标准
  • SpringBoot工程快速启动
  • Chatper 4: Implementing a GPT model from Scratch To Generate Text
  • 为独特工作流设计 K8s 健康检查(Design k8s Health Check for Unique Workflow)
  • citrix netscaler13.1 重写负载均衡响应头(基础版)
  • 如何利用.NET版PDF处理控件Aspose.PDF,使用 C# 从 PDF 中删除水印