设计模式汇总:查看
通俗示例
想象一下,你正在制作一个复杂的文档,这个文档包含了大量的文本、图片和格式设置。现在,你希望创建一个与当前文档内容完全相同的新文档,但又不希望花费时间重新编辑和排版。在这个时候,如果有一个"复制"按钮,你可以一键复制整个文档,并在新的副本上进行修改,这该多方便啊!这个"复制"按钮就是原型模式的现实例子。
通俗解释
原型模式是一种创建型设计模式,它允许我们通过复制现有的对象来创建新的对象,而不是通过传统的构造函数创建。这种模式特别适用于创建复杂对象时,可以避免初始化的复杂性,提高效率。
在原型模式中,我们有一个原型接口,它定义了复制自身的方法。具体原型类会实现这个接口,并提供复制自身的具体逻辑。当客户端需要创建一个新对象时,它不是直接实例化一个新对象,而是向一个原型对象请求一个复制自身的方法,从而获得一个新的对象。
原型模式的组成
- 抽象原型(Prototype):这是一个接口,用来声明复制自身的方法。
- 具体原型(Concrete Prototype):实现了抽象原型的接口,提供复制自身的具体实现。
- 客户端(Client):通过调用原型对象的复制方法来创建新的对象。
原型模式的两种复制方式
- 浅复制:只复制对象本身及其包含的值类型的成员变量,而对于引用类型的成员变量,浅复制会复制引用,不会复制引用指向的对象。
- 深复制:除了复制对象本身及其包含的值类型的成员变量外,还会复制引用类型的成员变量所指向的对象。
Python代码示例
下面是一个简单的原型模式实现示例
python
"""
比如:当我们出版了一本书《Python 设计模式 1.0版》,
若10 年后我们觉得这本书跟不上时代了,这时候需要去重写一本《Python 设计模式 2.0版》,
那么我们是完全重写一本书呢?还是在原有《Python 设计模式 1.0版》的基础上进行修改呢?
当然是后者,这样会省去很多排版、添加原有知识等已经做过的工作。
"""
import copy
from collections import OrderedDict
class Book:
def __init__(self, name, authors, price, **rest):
"""
rest的例子有:出版商、长度、标签、出版日期
"""
self.name = name
self.authors = authors
self.price = price
self.__dict__.update(rest) # 添加其他额外属性
def __str__(self):
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)
class Prototype:
def __init__(self):
self.objects = dict() # 初始化一个原型列表
def register(self, identifier, obj):
# 在原型列表中注册原型对象
self.objects[identifier] = obj
def unregister(self, identifier):
# 从原型列表中删除原型对象
del self.objects[identifier]
def clone(self, identifier, **attr):
# 根据 identifier 在原型列表中查找原型对象并克隆
found = self.objects.get(identifier)
if not found:
raise ValueError('Incorrect object identifier: {}'.format(identifier))
obj = copy.deepcopy(found)
obj.__dict__.update(attr) # 用新的属性值替换原型对象中的对应属性
return obj
def main():
"""
主函数,用于演示克隆技术的使用。
它创建了一本书的对象,并使用原型模式克隆了这本书的对象,修改了某些属性。
最后,打印出两个对象的信息和它们的内存地址,以证明它们是独立的对象。
"""
# 创建一本书的对象,初始化各种属性,如标题、作者、价格等
b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'),
price=118, publisher='Prentice Hall', length=228, publication_date='1978-02-22',
tags=('C', 'programming', 'algorithms', 'data structures'))
# 创建一个原型对象,用于后续的克隆操作
prototype = Prototype()
# 注册b1对象为原型,使用一个唯一标识符进行标记
cid = 'k&r-first'
prototype.register(cid, b1)
# 使用cid克隆b1对象,并修改克隆对象的一些属性,如名称、价格、长度等
b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99, length=274, publication_date='1988-04-01', edition=2)
# 遍历并打印两个对象的信息
for i in (b1, b2):
print(i)
# 打印两个对象的内存地址,以证明它们是不同的对象
print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2)))
if __name__ == '__main__':
main()
更符合python方式实现,deepcopy
python
def pythonic_main():
b1 = Book('The C Programming Language',
('Brian W. Kernighan', 'Dennis M.Ritchie'),
price=118,
publisher='Prentice Hall',
length=228,
publication_date='1978-02-22',
tags=('C', 'programming', 'algorithms', 'data structures'))
# 这里我们彻底抛弃之前的原型设计模式的写法
b2 = copy.deepcopy(b1)
b2.name = 'The C Programming Language(ANSI)'
b2.price = 48.99
b2.length = 274
b2.publication_date = '1988-04-01'
b2.edition = 2
for i in (b1, b2):
print(i)
print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2)))
总结
原型模式在处理复杂对象的复制时非常有用,它简化了对象的创建过程,并允许动态地改变对象的状态。不过,使用原型模式时,需要注意深复制和浅复制的区别,以及可能出现的循环引用问题。