《大话设计模式》——原型模式

什么是原型模式

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝、克隆)的方式,来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式,简称原型模式

核心:拷贝(克隆)

应用场景

创建对象包含的申请内存、给成员变量赋值这一过程,本身并不会花费太多时间,对于大部分业务系统来说,这点时间完全是可以忽略的。

但是,如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执行这些耗时的操作。

角色

  • Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
  • ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象
  • Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。

例子

通过个人信息类(Information)创建两个实例对象(zs、lf)。

抽象原型类如下:

class Information:
    """个人信息"""
    def __init__(self):
        self.name = None
        self.age = None
        self.height = None
        self.hobbies = list()

    def run(self):
        """自我介绍方法"""
        print("我叫{}: 年龄:{} 身高:{} 爱好: {}".format(self.name, self.age, self.height, self.hobbies))

具体原型类如下:

class Prototype:
    def __init__(self, obj):
        self.copy_object = obj()

    def clone(self, **attr):
        '''克隆对象'''
        obj = copy.deepcopy(self.copy_object)	# 深克隆
        obj.__dict__.update(attr)
        return obj

客户类

if __name__ == '__main__':
    people = Prototype(Information)	# 实例化一个具体原型
    zs = people.clone(name='张山', age="30", height='170cm', hobbies=['音乐', '读书'])
    zs.run()
    lf = people.clone(name='李飞', age="20", height='190cm', hobbies=['篮球', '羽毛球'])
    lf.run()

输出结果:

我叫张山: 年龄:30 身高:170cm 爱好: ['音乐', '读书']
我叫李飞: 年龄:20 身高:190cm 爱好: ['篮球', '羽毛球']

也可以换一种写法,写成如下形式:

import copy

class Information:
    """个人信息"""
    def __init__(self):
        self.name = None
        self.age = None
        self.height = None
        self.hobbies = list()

    def run(self):
        """自我介绍方法"""
        print("我叫{}: 年龄:{} 身高:{} 爱好: {}".format(self.name, self.age, self.height, self.hobbies))

if __name__ == '__main__':
    zs = copy.deepcopy(Information())
    zs.name = '张三'
    zs.age = "30"
    zs.height = '170cm'
    zs.hobbies = ['音乐', '读书']
    zs.run()

    lf = copy.copy(Information())
    lf.name = '李飞'
    lf.age = '20'
    lf.height = '190cm'
    lf.hobbies = ['篮球', '羽毛球']
    lf.run()

深克隆与浅克隆

上面例子中使用的是深克隆(深拷贝)。其实还有浅克隆(浅拷贝)。

浅克隆(浅拷贝)

  • 简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制

  • 通过copy.copy()实现。

深克隆(深拷贝)

  • 简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
  • 通过copy.deepcopy()实现。

浅克隆可能出现以下问题。举例

>>> import copy
>>> a = [1, "2", ['a',None]]
>>> b = copy.copy(a)	# 浅克隆
>>> b
[1, '2', ['a', None]]
>>> c = copy.deepcopy(a)	# 深克隆
>>> c
[1, '2', ['a', None]]

b浅克隆a,c深克隆a。

如果a修改了列表中嵌套的子列表中的内容,则b也会跟着变动,而深克隆的c却不会发生变化。

>>> a[2].append(1)	# 
>>> a
[1, '2', ['a', None, 1]]
>>> b				# 可以看出,b也被修改了
[1, '2', ['a', None, 1]]
>>> c
[1, '2', ['a', None]]

这是因为b只是克隆了a[2]指向的内存地址,而a[2]是一个列表,列表属于可变类型,因此,当a[2]发生变化时,b也跟着变动。这就是浅克隆的副作用;而c直接完全克隆了a中的值,并不会因为a发生变化而变化。

所以,一般建议使用深克隆。

打开微信扫一扫,关注【西加加先生】微信公众号,及时接收博文推送
在这里插入图片描述

posted @ 2020-02-28 18:27  西加加先生  阅读(192)  评论(0编辑  收藏  举报