聲明,本系列文章主要參考《精通Python設計模式》一書,並且參考一些資料,結合自己的一些看法來總結而來。
在《精通Python設計模式》中把設計模式分為三種類型:
- 創建型模式
- 結構型模式
- 行為型模式
本篇主要介紹關於 創建型模式的一種,書上的說法為:
當我們已有一個對象,並希望創建該對象的一個完整副本時,原型模式就派上用場了。在我們知道對象的某些部分會被變更但又希望保持原有對象不變之時,通常需要對象的一個副本。在這樣的案例中,重新創建原有對象是沒有意義的(請參考網頁[ Mitotic division ])。
另一個案例是,當我們想復制一個復雜對象時,使用原型模式會很方便。對於復制復雜對象,我們可以將對象當作是從數據庫中獲取的,並引用其他一些也是從數據庫中獲取的對象。若通過多次重復查詢數據來創建一個對象,則要做很多工作。在這種場景下使用原型模式要方便得多。
個人理解:
當我們已經存在一個對象,這個對象有其屬性和方法,若我們還想去獲得另外一個同類型對象,此時有兩種選擇:重新去創建一個新的對象,或者 根據已有的對象復制一個副本,而在很多時候我們不需要完全去重新構建一個對象,只需要在原有對象存在的基礎上(保留原對象),去修改其屬性和方法得到一個新的對象。
其實通俗解釋來說:
比如:當我們出版了一本書《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() 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 : 2378797084512 != ID b2 : 2378796684008
其實這段代碼在 Python 中,我們要實現一樣的效果,並沒有這么復雜,完全可以不使用這樣的方法,我們更熟悉的方法是這樣的,這里只修改 main 函數:
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')) # 這里我們徹底拋棄之前的原型設計模式的寫法 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)))
所以說:同樣的內容,經過不同的方式,可以得到同樣的效果。
《Python設計模式》中也提及到,大概意思如下:
設計模式不會綁定具體的編程語言。一個好的設計模式應該能夠用大部分編程語言實現(如果做不到全部的話,具體取決於語言特性)。最為重要的是,設計模式也是一把雙刃劍,如果設計模式被用在不恰當的情形下將會造成災難,進而帶來無窮的麻煩。然而如果設計模式在正確的時間被用在正確地地方,它將是你的救星。
這里不是說書中實例的原型模式沒有用,而是說 有些時候可以用簡單粗暴的方式實現,何必整的那么彎彎繞繞呢?當然,視情況而定吧。
over~~~~~~~~