創建型--克隆模式

什麼是克隆模式


克隆模式: 也稱之爲 原型模式,顧名思義,就是創建一個實例對象的副本。創建副本的過程中,分爲淺副本深副本兩種情況。

  • 淺副本: 淺副本與原對象的某些屬性共用一片內存接口,這是一種引用操作;
  • 深副本: 深副本的所有屬性內存與原對象的所有屬性接口不一樣,深副本的數據拷貝自原對象;

具體區別如下所示:

在這裏插入圖片描述

什麼時候使用淺副本:如果資源有限(例如嵌入式系統)或者性能至關重要(例如高性能計算),使用淺複製 會更好,它可以實現數據共享、減少克隆對象的創建時間;
什麼時候使用深副本:我們需要一個對象的完整副本。


爲什麼不選擇重新創建一個實例對象,而選擇克隆模式


  • 如果重新創建一個實例對象,我們得到的只是一個原始狀態的實例對象;
  • 克隆模式獲取的是當前狀態下實例對象的副本;
  • 兩者操作獲取的實例對象的狀態信息是不一樣的。

克隆模式的應用場景


  • 當我們已有一個對象,並希望創建該對象的一個完整副本時,原型模式就派上用場了;
  • 當我們想複製一個複雜對象時,使用原型模式會很方便;避免了重新創建對象,並對數據庫進行多次查詢操作;

克隆模式的例子


例子1:深度複製:deepcopy函數

Python 有專門的庫函數來實現深度複製,例如:copy.deepcopy()

import copy

class A:
    def __init__(self):
        self.x = 18
        self.msg = 'Hello'

class B(A):
    def __init__(self):
        super().__init__()
        self.y = 34

    def __str__(self):
        return '{}, {}, {}'.format(self.x, self.msg, self.y)

if __name__ == '__main__':
    b = B()
    c = copy.deepcopy(b)
    print([str(i) for i in (b, c)]) # 打印屬性數據
    print([i for i in (b, c)]) # 打印實例地址

輸出的結果:

['18, Hello, 34', '18, Hello, 34']
[<__main__.B object at 0x7f4764bdbcf8>, <__main__.B object at 0x7f4764bdbda0>]

例子2:克隆書籍

假設某本書第一版的信息包括:書名,作者,價格等。現在要對該書進行再版,新版和舊版之間絕大部分的信息都是一樣的。一個很好的辦法就是對第一版的信息進行克隆,在副本的基礎之上進行修改。


定義書籍類

在下述代碼中,Book類展示了一種有趣的技術可避免可伸縮構造器問題。在 __init__() 方法中,僅有三個形參是固定的: name、 authors、price,但是使用 rest 變長列表,調用者能以關鍵詞的形式(名稱=值)傳入更多的參數。 self.__dict__.update(rest) 一行將 rest 的內容添加到 Book 類的內部字典中,成爲它的一部分。
代碼中還使用了 OrderedDict 來強制元素有序。

**加載公共庫**
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)

定義克隆類

Prototype 類實現了原型設計模式。 Prototype類的核心是clone()方法,該方法使用我們熟悉的copy.deepcopy()函數來完成真正的克隆工作。但Prototype類在支持克隆之外做了一點更多的事情,它包含了方法 register()unregister(),這兩個方法用於在一個字典中追蹤被克隆的對象。

# 克隆類
class Prototype:

    def __init__(self):
        self.objects = dict() # 存儲對象 

    # 登記要克隆的對象
    def register(self, identifier, obj):
        """
        Params:
            identifier:需要克隆對象的標識;
            obj:需要克隆的對象
        """
        self.objects[identifier] = obj

    # 在克隆集合中刪除指定對象
    def unregister(self, identifier):
        del self.objects[identifier]

    # 返回克隆後的對象
    def clone(self, identifier, **attr):
        """
        Params:
            - identifier:對象標識;
            - **attr:需要被更新的屬性以及屬性數據
        """
        found = self.objects.get(identifier) # 根據標識獲取對象
        if not found:
            raise ValueError('Incorrect object identifier: {}'.format(identifier))
        obj = copy.deepcopy(found) # clone 對象
        obj.__dict__.update(attr) # 更新副本的參數
        return obj

實現克隆操作

  • 首先建立了一個書籍對象 b1;
  • 把 b1 登記進入克隆實例對象中;
  • 選擇克隆書籍對象 b1;
  • 對比 b1 和其副本的 id;
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()
    cid = 'k&r-first'
    prototype.register(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()

運行的結果如下:

結果:
authors: ('Brian W. Kernighan', 'Dennis M.Ritchie')
length: 228
name: The C Programming Language
price: 118$
publication_date: 1978-02-22
publisher: Prentice Hall
tags: ('C', 'programming', 'algorithms', 'data structures')

authors: ('Brian W. Kernighan', 'Dennis M.Ritchie')
edition: 2
length: 274
name: The C Programming Language(ANSI)
price: 48.99$
publication_date: 1988-04-01
publisher: Prentice Hall
tags: ('C', 'programming', 'algorithms', 'data structures')

ID b1 : 139993865162592 != ID b2 : 139993865255400

源碼在這裏;


參考


  1. 《精通Python設計模式》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章