欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > 设计模式学习手册(四)(原型模式)

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

2025/2/25 4:41:04 来源:https://blog.csdn.net/qq_38212260/article/details/145116583  浏览:    关键词:设计模式学习手册(四)(原型模式)

写在前面

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

原型模式的简单尝试

  • 先尝试有一种传统的写法,需要不断的创建新的实例,当构造函数涉及到的属性很多后,这种方式就显得效率低下了
class person:def __init__(self, name, age):self.name = nameself.age = agedef 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 = ac = aa.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 = nameself.interests = interests  def get_name(self):return self.namedef get_interests(self):return self.interestsdef 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 = nameself.interests = interests  def get_name(self):return self.namedef get_interests(self):return self.interestsdef 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 = nameself.interests = interestsdef get_name(self):return self.namedef get_interests(self):return self.interestsdef clone(self):# 返回一个克隆对象return copy.deepcopy(self)# 工厂类,用于生产对象
class PrototypeFactory:def __init__(self):# 初始化时可以设置多个原型对象self.prototypes = {}def register_prototype(self, name, prototype):""" 注册原型对象 """self.prototypes[name] = prototypedef 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() 方法来创建对象实例,而不直接使用构造函数

最后

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词