原型模式(Prototype Pattern):复制已有的对象,实现功能复用和优化
1 介绍
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
主要解决:在运行期建立和删除原型。
何时使用:
- 当一个系统应该独立于它的产品创建,构成和表示时。
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
- 为了避免创建一个与产品类层次平行的工厂类层次时。
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
2 适用场景
- 资源优化场景。
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景。
- 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
3 实现步骤
原型设计模式(Prototype design pattern)帮助我们创建对象的克隆,其最简单的形式就是一个clone()函数,接受一个对象作为输入参数,返回输入对象的一个副本。拷贝分为深拷贝和浅拷贝,使用python内置的copy模块实现。深拷贝copy.deepcopy()函数,会递归复制并创建新对象;而浅拷贝copy ()函数会利用引用指向同一个对象。深拷贝的优点是对象之间互不影响,但是会耗费资源,创建比较耗时;如果不会修改对象可以使用浅拷贝,更加节省资源和创建时间。
在Python中,可以使用copy.copy()函数进行浅复制。以下内容引用自Python官方文档,说明了浅副本(copy.copy())和深副本(copy.deepcopy())之间的区别:
- 浅副本构造一个新的复合对象后,(会尽可能地)将在原始对象中找到的对象的引用插入新对象中。
- 深副本构造一个新的复合对象后,会递归地将在原始对象中找到的对象的副本插入新对象中。
浅拷贝,我们关注提升应用性能和优化内存使用,在对象之间引入数据共享,但需要小心地修改数据,因为所有变更对所有副本都是可见的。浅副本在本章中没有过多介绍,但也许你会想试验一下。
深拷贝,我们希望能够对一个副本进行更改而不会影响其他对象。对于我们之前看到的蛋糕食谱示例这类案例,这一特性是很有用的。这里不会进行数据共享,所以需要关注因对象克隆而引入的资源耗用问题。
使用步骤:
步骤一:定义object复制类Prototype
步骤二:定义产品类
示例代码如下:
import copy
“”“
步骤1: 定义object复制类Prototype
”“”
class Prototype(object):
def __init__(self):
self._objects = {}
def register_object(self, name, obj):
"""
注册对象实例
:param name: obj_name -> str
:param obj: object -> object
"""
self._objects[name] = obj
def unregister_object(self, name):
"""
删除对象
:param name: object_name -> str
"""
del self._objects[name]
def clone(self, name, **attrs):
"""
从已存在的实例深拷贝一个新实例
:param name: 被copy的实例
:param attrs: 新对象属性
:return: 新对象
"""
old_obj = self._objects.get(name)
obj_new = copy.deepcopy(old_obj)
obj_new.__dict__.update(attrs)
return obj_new
“”“
步骤2: 定义产品类
”“”
class Demo(object):
value = "Default"
def run():
instance_a = Demo()
prototype = Prototype()
prototype.register_object('instance_a', instance_a)
instance_b = prototype.clone('instance_a', value="Hello", test="demo")
print(instance_a.value)
print(instance_b.value, instance_b.test)
if __name__ == "__main__":
run()
代码输出:
Default
Hello demo
4 代码实践
案例一:展示书籍信息的应用
很多畅销书籍会经历多个版本的迭代。
有变化的信息:价格、长度(页数)以及出版日期。
但也有很多相似之处:作者、出版商以及描述该书的标签/关键词都是完全一样的。这表明从头创建一版新书并不总是最佳方式。
如果知道两个版本之间的诸多相似之处,则可以先克隆一份,然后仅修改新版本与旧版本之间的不同之处。
import copy
from collections import OrderedDict
class Prototype(object):
def __init__(self):
self._objects = {}
def register_object(self, name, obj):
"""
注册对象实例
:param name: obj_name -> str
:param obj: object -> object
"""
self._objects[name] = obj
def unregister_object(self, name):
"""
删除对象
:param name: object_name -> str
"""
del self._objects[name]
def clone(self, id, **attrs):
"""
从已存在的实例深拷贝一个新实例
:param name: 被copy的实例
:param attrs: 新对象属性
:return: 新对象
"""
old_obj = self._objects.get(id)
if not old_obj:
raise ValueError("Incorrect object name")
obj_new = copy.deepcopy(old_obj)
obj_new.__dict__.update(attrs)
return obj_new
class Book(object):
def __init__(self, name, authors, price, **kwargs):
"""
初始化书籍公用信息
:param name: 书名
:param authors: 作者
:param price: 价格
:param kwargs: 其他信息
"""
self.name = name
self.authors = authors
self.price = price
self.__dict__.update(kwargs)
def __str__(self):
"""
调用print()时自动打印书籍信息
"""
mylist = []
ordered = OrderedDict(sorted(self.__dict__.items()))
for i in ordered.keys():
mylist.append('{}: {}'.format(i, ordered[i]))
if i == 'price':
mylist.append('$')
mylist.append('\n')
return ''.join(mylist)
def run():
book_1 = Book("Python核心编程(第一版)", "宋吉广", "45.9", publisher="邮电出版社", length="350页")
prototype = Prototype()
prototype.register_object('book_1', book_1)
book_2 = prototype.clone('book_1', name="Python核心编程(第二版)", authors="宋吉广",
price="75.9", publisher="邮电出版社", length="550页")
print(book_1)
print(book_2)
if __name__ == "__main__":
run()
代码输出:
authors: 宋吉广
length: 350页
name: Python核心编程(第一版)
price: 45.9$
publisher: 邮电出版社
authors: 宋吉广
length: 550页
name: Python核心编程(第二版)
price: 75.9$
publisher: 邮电出版社
参考文献:
https://www.runoob.com/design-pattern/prototype-pattern.html