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

写在前面

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

原型模式的简单尝试

  • 先尝试有一种传统的写法,需要不断的创建新的实例,当构造函数涉及到的属性很多后,这种方式就显得效率低下了
python 复制代码
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也会同时变化
python 复制代码
if __name__ == '__main__':
    a = person("John", 25)
    b = a
    c = a

    a.name = "Jane"

    print(a.todict())
    print(b.todict())
    print(c.todict())
  • 使用原型模式
python 复制代码
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()方法用于复制对象。
    • 具体原型类:实现原型接口的类,负责实现具体的克隆逻辑。

原型模式之深拷贝

浅拷贝

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

python 复制代码
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函数即可
  • 深度拷贝则会递归地拷贝原对象中的每一个子对象,因此拷贝后的对象和原对象互不相关
python 复制代码
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
python 复制代码
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() 方法来创建对象实例,而不直接使用构造函数

最后

  • 原型模式理解起来应该不算复杂,核心就是拷贝
  • 原型模式往往用于类初始化非常复杂的场景,或者一个对象要被多个修改者操作的场景
  • 原型模式很少单独使用,学会将原型模式结合其他的设计模式一起使用。在项目中要多思考多尝试。
相关推荐
晨米酱5 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机10 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机11 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机11 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤11 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤1 天前
工厂模式
设计模式