通俗 Python 設計模式——原型設計模式
從本文開始,將會寫一系列關於Python 設計模式
通俗講解的文章,主要參考《Python 設計模式》一書,順序可能與原書目錄順序有所不同,因為我本身也是一邊學習一邊總結。本系列文章的主旨是剝開復雜的理論外殼,用最通俗的方法來講解Python 設計模式
。並且,通過修改程式碼為更簡潔的形式,來更好的破除_**專業詞彙迷信**_。
《Python 設計模式》一書將各種設計模式做了一個簡單分類:
- 建立型模式
- 結構型模式
- 行為型模式
今天要討論的是第一種——建立型模式——中的原型設計模式。
根據書上的說法,原型模式的作用是:
當我們已有一個物件,並希望建立該物件的一個完整副本時,原型模式就派上用場了。在我們知道物件的某些部分會被變更但又希望保持原有物件不變之時,通常需要物件的一個副本。在這樣的案例中,重新建立原有物件是沒有意義的(請參考網頁[ http://t.cn/RqBrOuM ])。
另一個案例是,當我們想複製一個複雜物件時,使用原型模式會很方便。對於複製複雜物件,我們可以將物件當作是從資料庫中獲取的,並引用其他一些也是從資料庫中獲取的物件。若通過多次重複查詢資料來建立一個物件,則要做很多工作。在這種場景下使用原型模式要方便得多。
這一段描述太抽象,其實用最簡單的話來說就是:
第一種情況,你有一個
ApplePen
,想要一個一模一樣的ApplePen
,可以直接製作一個ApplePen
的副本。第二種情況,你有一個
ApplePen
,想要一個PineapplePen
,可以通過製作一個ApplePen
的副本,然後修改這個副本為PineapplePen
的方式來達成目的。第三種情況,你有一個非常複雜的
PenPineappleApplePen
,然後你需要一個PineapplePenApplePen
,多次操作可能會非常麻煩,那麼可以以PenPineappleApplePen
為原型,建立一個副本PenPineappleApplePen
,再通過簡單的幾步就能將其修改為PineapplePenApplePen
。(其實一般情況下與第二種情況沒差)
看起來好像還是很複雜?無所謂,把每種情況中,看不懂的 | 第一個 | 多次出現的 | 單詞改為A
,看不懂的 | 第二個 | 多次出現的 | 單詞改為B
,就明白了。
好,我們現在把概念用最簡單的話梳理清楚了,那麼我們來看看程式碼實現。書上給出了這樣一個例子:
一本書,第一版出版了。10年後,第二版出版了,有一定的修改。如何使用原型模式建立一個展示圖書資訊的應用?
這裡其實可以看做我們總結出來的第二種情況,即:在有了原型物件的情況下,如何通過使用原型設計模式的編碼,來得到新的物件。
我們先看看書上給出的示例程式碼,原書程式碼中沒有註釋,我嘗試補充了一些註釋,也許會有一定的錯漏:
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()
我們將這段程式碼儲存為prototype.py
,在控制檯執行,可以得到如下輸出:
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 的朋友可以發現,其實這段程式碼在 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 的靈活性,其實在一般情況下沒必要搞得那麼費勁,用最簡單粗暴的方式就可以達成我們想要的目標。當然,這裡並不是說原型模式就沒用,各位根據情況,靈活處理就好。
相關文章
- 通俗 Python 設計模式——代理模式Python設計模式
- 通俗 Python 設計模式——工廠模式Python設計模式
- 通俗 Python 設計模式——外觀模式Python設計模式
- 通俗 Python 設計模式——建造者模式Python設計模式
- 通俗 Python 設計模式——享元模式Python設計模式
- 通俗 Python 設計模式——介面卡模式Python設計模式
- 設計模式-原型模式設計模式原型
- 設計模式 —— 原型模式設計模式原型
- 設計模式(原型模式)設計模式原型
- 設計模式:原型模式設計模式原型
- 設計模式 - 原型模式設計模式原型
- 23種設計模式(八)-原型設計模式設計模式原型
- Java設計模式---原型模式Java設計模式原型
- 設計模式(三)——原型模式設計模式原型
- java設計模式—原型模式Java設計模式原型
- 設計模式【5】-- 原型模式設計模式原型
- Java設計模式-原型模式Java設計模式原型
- 設計模式(十六)原型模式設計模式原型
- 設計模式之原型模式設計模式原型
- 設計模式-原型模式(Prototype)設計模式原型
- 設計模式之原型設計模式原型
- golang設計模式之原型模式Golang設計模式原型
- GoLang設計模式05 - 原型模式Golang設計模式原型
- 設計模式--原型模式(Prototype Pattern)設計模式原型
- go設計模式之原型模式Go設計模式原型
- 設計模式入門:原型模式設計模式原型
- 極簡設計模式-原型模式設計模式原型
- Java設計模式之原型模式Java設計模式原型
- 設計模式學習-原型模式設計模式原型
- 【大話設計模式】—— 原型模式設計模式原型
- 設計模式之-原型模式-Prototype設計模式原型
- Java設計模式5:原型模式Java設計模式原型
- 通俗易懂的設計模式設計模式
- 設計模式 #3 (原型模式、建造者模式)設計模式原型
- 【設計模式(四)】建立型模式--原型模式設計模式原型
- 設計模式(Java語言)- 原型模式設計模式Java原型
- 23種設計模式-原型模式(3)設計模式原型
- Java設計模式之(四)——原型模式Java設計模式原型