写在前面
- 书接上文设计模式学习手册(三)(建造者模式)
- 原型模式简单来说就是复制一个已存在的原型实例,并对其进行必要的修改,来创建新的对象。原型模式通常会有一个
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
属性,prototype1
和prototype2
都受到了影响。主要原因是:浅拷贝中的元素,只拷贝了表面的一层,因此,如果原对象中包含了引用对象,改变其也会影响拷贝后的对象
深拷贝
- 只要修改
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()
方法来创建对象实例,而不直接使用构造函数
最后
- 原型模式理解起来应该不算复杂,核心就是拷贝
- 原型模式往往用于类初始化非常复杂的场景,或者一个对象要被多个修改者操作的场景
- 原型模式很少单独使用,学会将原型模式结合其他的设计模式一起使用。在项目中要多思考多尝试。