什么是原型模式?
原型模式,就是“克隆”对象。
当一个现有对象需要保持不变,而我们想创建它的精确副本,以便更改副本的某些部分时,原型模式非常有用。
Python中的原型模式
Python天然就有使用原型模式的优势,使用copy.deepcopy()就能实现对象的完全复制(深拷贝)。
import copy
obj1 = object()
obj2 = copy.deepcopy(obj1)
print(id(obj1), id(obj2)) # 输出的两个对象的id是不一样的
原型模式示例
我们有一个Website类,用于保存网站所有有用的信息,如名称、域名、描述、我们管理的网站的作者,等等。我们每次建立新的网站而想修改所有信息时,可以在之前网站的基础上建立副本。
import copy
class Website:
def __init__(self, name, domain, description, author, **kwargs):
self.name, self.domain, self.description, self.author = name, domain, description, author
for key in kwargs:
setattr(self, key, kwargs[key])
def __str__(self):
infos = vars(self).items()
return '\n'.join([f'{k}:{v}' for k, v in infos])
class Prototype:
def __init__(self):
print("__init__() be call!")
self.objects = dict()
def register(self, obj_id, obj):
self.objects[obj_id] = obj
def unregister(self, obj_id):
del self.objects[obj_id]
def clone(self, obj_id, **attrs):
old_obj = self.objects.get(obj_id)
if not old_obj:
raise ValueError(f'Incorrect object identifier: {obj_id}')
new_obj = copy.deepcopy(old_obj)
for key in attrs:
setattr(new_obj, key, attrs[key])
return new_obj
if __name__ == '__main__':
site_id = 'qq'
site_1 = Website(name='website1',domain='spade.com', description='apps store', author='spade', used='True')
prototype = Prototype()
prototype.register(site_id, site_1)
site_2 = prototype.clone(site_id, name='qq', domain='qq.com', description='qq', boss='mahuateng')
print(site_1)
print(id(site_1))
print()
print(site_2)
print(id(site_2))
输出结果:
__init__() be call!
name:website1
domain:spade.com
description:apps store
author:spade
used:True
2293159517832
name:qq
domain:qq.com
description:qq
author:spade
used:True
boss:mahuateng
2293159852808
我们可以看到关于原始Website对象及其克隆体的信息。查看id()函数的输出,可以看到这两个地址是不同的。
为什么要使用原型模式?
此外,我们还经常需要复制从数据库取出的对象,这些对象通常引用其他基于数据库的对象。克隆这样一个复杂的对象成本很高(需要对数据库进行多次查询),所以原型是解决这个问题的一种便捷方法。
原型模式的优点
- 性能优良原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
- 逃避构造函数的约束这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
原型模式的使用场景
-
资源优化场景类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
-
性能和安全要求的场景通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
-
一个对象多个修改者的场景一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
参考书籍
《设计模式之禅(第2版)》《精通Python设计模式(第2版)》