Python 提供了許多設計模式,可以幫助我們編寫更簡潔、更易維護的程式碼。本文將介紹 Memento、迭代器和範本方法三種設計模式,並提供 Python 程式碼範例。Memento 模式允許我們儲存和還原物件的狀態,這在需要實作復原功能時非常有用。迭代器模式提供了一種標準的方式來遍歷集合中的元素,而無需暴露集合的底層實作。範本方法模式則定義了一個演算法的骨架,允許子類別實作特定的步驟,而無需修改演算法的整體結構。這些模式在實際開發中都有廣泛的應用,可以有效提高程式碼的可讀性和可維護性。
Memento 設計模式:物件狀態的快照與還原
在許多情況下,我們需要一種方法來輕鬆地取得物件內部狀態的快照,以便在需要時能夠還原該物件。Memento 設計模式可以幫助我們實作這種需求。
Memento 模式的三個關鍵元件
- Memento:一個簡單的物件,包含基本的狀態儲存和檢索功能。
- Originator:一個能夠取得和設定 Memento 例項值的物件。
- Caretaker:一個能夠儲存和檢索先前建立的所有 Memento 例項的物件。
Memento 模式與 Command 模式有許多相似之處。
真實世界的例子
在現實生活中,Memento 模式可以在許多情況下看到。例如,語言詞典會定期更新,新詞被加入,而一些詞彙則變得過時。語言會隨著時間演變,官方詞典需要反映這種變化。我們有時會查閱以前的版本,以瞭解語言在過去某個時間點的使用方式。這對於理解特定領域的某些內容非常有用。研究人員可能會使用舊詞典或查閱檔案,以找到有關某些詞彙和表達方式的資訊。
另一個例子是 Zope(http://www.zope.org),它具有一個整合的物件資料函式庫,稱為 Zope 物件資料函式庫(ZODB)。ZODB 為 Python 提供了一個物件資料函式庫,被廣泛用於 Pyramid 和 Plone 等技術堆積疊中。
Memento 模式的使用案例
Memento 模式通常用於需要為使用者提供某種形式的復原和重做功能的情況。另一個使用案例是實作具有 OK/Cancel 按鈕的 UI 對話方塊,我們會在載入時儲存物件的狀態,如果使用者選擇取消,我們會還原物件的初始狀態。
實作 Memento 模式
我們將以一種簡化的方式來實作 Memento 模式,並且按照 Python 語言的自然方式進行。這意味著我們不一定需要多個類別。
我們將使用 Python 的 pickle 模組。pickle 用於什麼?根據該模組的檔案(https://docs.python.org/3/library/pickle.html),pickle 模組可以將複雜的物件轉換為位元組串流,並且可以將位元組串流轉換回具有相同內部結構的物件。
警告:pickle 模組在此處用於演示目的,但您應該知道它不適用於一般用途的安全性。
讓我們考慮一個具有 text 和 author 屬性的 Quote 類別。要建立 memento,我們將在該類別上使用一個方法 save_state(),該方法將使用 pickle.dumps() 函式來傾印物件的狀態,從而建立 memento:
class Quote:
def __init__(self, text, author):
self.text = text
self.author = author
def save_state(self):
current_state = pickle.dumps(self.__dict__)
return current_state
def restore_state(self, memento):
previous_state = pickle.loads(memento)
self.__dict__.clear()
self.__dict__.update(previous_state)
def __str__(self):
return f"{self.text}\n- By {self.author}."
程式碼解密:
Quote類別被定義,具有text和author屬性。save_state方法使用pickle.dumps()將物件的狀態序列化為位元組串流。restore_state方法使用pickle.loads()將位元組串流反序列化回物件狀態,並更新物件屬性。__str__方法傳回 Quote 物件的字串表示形式。
主函式測試實作
def main():
print("** Quote 1 **")
q1 = Quote(
"A room without books is like a body without a soul.",
"Unknown author",
)
print(f"\nOriginal version:\n{q1}")
q1_mem = q1.save_state()
# 現在,我們找到了作者的名字
q1.author = "Marcus Tullius Cicero"
print(f"\nWe found the author, and did an updated:\n{q1}")
# 還原先前的狀態(復原)
q1.restore_state(q1_mem)
print(f"\nWe had to restore the previous version:\n{q1}")
print()
print("** Quote 2 **")
text = (
"To be you in a world that is constantly \n"
"trying to make you be something else is \n"
"the greatest accomplishment."
)
q2 = Quote(
text,
"Ralph Waldo Emerson",
)
print(f"\nOriginal version:\n{q2}")
_ = q2.save_state()
# 對文字進行更改
q2.text = (
"To be yourself in a world that is constantly \n"
"trying to make you something else is the greatest \n"
"accomplishment."
)
程式碼解密:
- 建立兩個
Quote物件,分別為q1和q2。 - 對
q1和q2進行狀態儲存和還原操作,以演示 Memento 模式的功能。 - 輸出結果顯示了原始版本、更新版本和還原版本之間的變化。
範例執行結果
** Quote 1 **
Original version:
A room without books is like a body without a soul.
- By Unknown author.
We found the author, and did an updated:
A room without books is like a body without a soul.
- By Marcus Tullius Cicero.
We had to restore the previous version:
A room without books is like a body without a soul.
- By Unknown author.
** Quote 2 **
Original version:
To be you in a world that is constantly
trying to make you be something else is
the greatest accomplishment.
- By Ralph Waldo Emerson.
迭代器模式(Iterator Pattern)
在程式設計中,序列或物件集合的使用非常普遍,尤其是在演算法和資料處理程式中。迭代器模式是一種在處理物件集合時非常有用的設計模式。
迭代器模式的定義
根據維基百科的定義,迭代器是一種設計模式,其中迭代器用於遍歷容器並存取容器的元素。迭代器模式將演算法與容器分離;在某些情況下,演算法必然與容器相關,因此無法分離。
迭代器模式的使用案例
在以下情況下,使用迭代器模式是一個好主意:
- 使導航遍歷集合變得容易
- 在任何時候取得集合中的下一個物件
- 在遍歷集合完成時停止
實作迭代器模式
Python 中已經實作了迭代器模式,用於 for 迴圈、列表推導式等。Python 中的迭代器是一個可以被迭代的物件,每次傳回一個元素。
我們可以透過實作迭代器協定(Iterator Protocol)來建立自己的迭代器,即實作 __iter__() 和 __next__() 兩個特殊方法。
程式碼範例:
class FootballTeamIterator:
def __init__(self, members):
self.members = members
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.members):
val = self.members[self.index]
self.index += 1
return val
else:
raise StopIteration()
class FootballTeam:
def __init__(self, members):
self.members = members
def __iter__(self):
return FootballTeamIterator(self.members)
def main():
members = [f"player{str(x)}" for x in range(1, 23)]
members = members + ["coach1", "coach2", "coach3"]
team = FootballTeam(members)
team_it = iter(team)
try:
while True:
print(next(team_it))
except StopIteration:
print("(End)")
#### 內容解密:
1. `FootballTeamIterator` 類別是迭代器類別,用於遍歷 `FootballTeam` 物件。
2. `__init__` 方法初始化迭代器物件與索引。
3. `__iter__` 方法傳回迭代器物件本身。
4. `__next__` 方法傳回集合中的下一個元素,如果到達末尾則引發 `StopIteration` 例外。
5. `FootballTeam` 類別代表一個足球隊,包含一個 `__iter__` 方法傳回對應的迭代器物件。
### 輸出結果:
player1 player2 … coach3 (End)
### 範本方法模式(Template Pattern)
範本方法模式旨在消除程式碼冗餘。在物件導向程式設計中,方法和函式是避免寫冗餘程式碼的重要工具。
#### 真實世界的例子:
作業員的日常例行工作是範本方法模式的典型例子。所有作業員遵循相同的例行工作,但具體的部分卻有所不同。
#### 使用案例:
* 當演算法具有結構上的相似性,但部分實作細節不同時,使用範本方法模式可以消除重複的程式碼。
* 分頁演算法是一個很好的使用案例,可以將其分為抽象(不變)和具體(變異)的部分。
## 行為設計模式:範本模式的應用與實踐
在軟體開發領域,設計模式是解決常見問題的通用解決方案。其中,範本模式(Template Pattern)是一種行為設計模式,它定義了一個操作的框架,將某些步驟的實作延遲到子類別中。
### 範本模式的實作
範本模式的核心思想是定義一個範本方法,該方法提供了一個演算法的框架,允許子類別在不改變演算法結構的情況下,重新定義某些步驟。在本例中,我們將實作一個橫幅生成器(banner generator),它能夠根據輸入的文字和樣式生成不同的橫幅。
#### 範本方法的定義
```python
def generate_banner(msg, style):
print("-- start of banner --")
print(style(msg))
print("-- end of banner --\n")
generate_banner 函式是我們的範本方法,它接受兩個引數:要顯示的文字(msg)和樣式(style)。該函式首先列印橫幅的開始部分,然後呼叫傳入的 style 函式來處理 msg,最後列印橫幅的結束部分。
不同樣式的實作
我們定義了三種不同的樣式函式:dots_style、admire_style 和 cow_style。
def dots_style(msg):
msg = msg.capitalize()
ten_dots = "." * 10
msg = f"{ten_dots}{msg}{ten_dots}"
return msg
def admire_style(msg):
msg = msg.upper()
return "!".join(msg)
def cow_style(msg):
msg = cow.milk_random_cow(msg)
return msg
dots_style將輸入文字首字母大寫,並在前後各新增10個點。admire_style將輸入文字轉為大寫,並在每個字母之間插入感嘆號。cow_style使用cowpy函式庫生成隨機的ASCII藝術字元。
主函式與執行結果
def main():
styles = (dots_style, admire_style, cow_style)
msg = "happy coding"
for style in styles:
generate_banner(msg, style)
if __name__ == "__main__":
main()
執行 main 函式,將使用不同的樣式生成並列印橫幅。
程式碼解析
- 匯入必要的函式庫:我們匯入了
cow函式來自cowpy函式庫,用於生成ASCII藝術字元。 - 定義範本方法:
generate_banner函式是範本方法的實作,它呼叫了傳入的樣式函式。 - 實作不同樣式:我們定義了三個樣式函式,每個函式對輸入文字進行不同的處理。
- 主函式:在
main函式中,我們定義了要使用的樣式列表和輸入文字,然後迴圈呼叫generate_banner函式生成不同樣式的橫幅。
內容解密:
- 範本模式提供了一個演算法的框架,允許子類別在不改變演算法結構的情況下,重新定義某些步驟。
- 在本例中,
generate_banner是範本方法,它定義了生成橫幅的基本步驟。 - 不同樣式的實作(如
dots_style、admire_style和cow_style)是對範本方法的擴充套件和自定義。 - 使用範本模式,可以輕鬆地新增新的樣式或修改現有樣式,而無需更改範本方法的實作。
行為設計模式綜覽與架構設計模式初探
在前一章中,我們探討了行為設計模式,這些模式幫助我們處理物件之間的互動和演算法。現在,我們將進一步探索架構設計模式,這些模式為解決常見的架構問題提供了範本,促進了可擴充套件、可維護和可重複使用系統的開發。
行為設計模式回顧
行為設計模式涵蓋了多種不同的模式,包括狀態模式(State Pattern)、直譯器模式(Interpreter Pattern)、策略模式(Strategy Pattern)、備忘錄模式(Memento Pattern)、迭代器模式(Iterator Pattern)和範本模式(Template Pattern)等。
狀態模式與有限狀態機
狀態模式利用狀態圖來實作有限狀態機,解決了許多計算和非計算問題。Python中的state_machine模組簡化了狀態機的建立和轉換前後的動作定義。
直譯器模式與領域特定語言
直譯器模式提供了一個類別似程式設計的框架給進階使用者和領域專家,而不暴露程式設計語言的複雜性。這是透過實作領域特定語言(DSL)來實作的。我們使用pyparsing建立了一個控制智慧家居的DSL,並展示瞭如何使用模式匹配來簡化結果的解釋。
策略模式與演算法選擇
策略模式用於當我們希望能夠透明地使用多種解決方案來解決同一個問題時。由於沒有完美的演算法能夠適用於所有輸入資料和情況,策略模式允許我們動態地決定在每種情況下使用哪種演算法。我們透過實作兩種不同的演算法來檢查單詞中的所有字元是否唯一,展示了Python如何利用其第一級函式簡化策略模式的實作。
備忘錄模式與狀態儲存
備忘錄模式用於在需要時儲存物件的狀態,提供了一種實作復原功能的效率解決方案。我們使用了一個簡化的例子,展示瞭如何使用Python標準函式庫中的pickle模組來儲存和還原資料物件的先前狀態。
迭代器模式與序列遍歷
迭代器模式提供了一種優雅且有效的方式來遍歷物件序列和集合。在Python中,迭代器是一種語言特性,可以直接用於內建容器,如列表和字典,並且可以透過實作Python迭代器協定來定義新的可迭代和迭代器類別。
範本模式與冗餘程式碼消除
範本模式用於消除在實作具有結構相似性的演算法時的冗餘程式碼。我們透過一個生成橫幅的例子展示瞭如何使用範本函式來實作自定義文字樣式。
架構設計模式初探
接下來,我們將探討架構設計模式,這些模式幫助解決常見的架構問題。
模型-檢視-控制器(MVC)模式
MVC模式是一種廣泛使用的架構設計模式,用於將應用程式邏輯、表示邏輯和控制邏輯分離。
微服務架構模式
微服務架構是一種將應用程式分解為一系列小的、獨立的服務的架構風格。每個微服務負責應用程式中的特定功能,並可以獨立開發、測試和佈署。
微服務實作技術需求
要實作微服務架構,需要安裝以下工具:
- gRPC:使用命令
python -m pip install grpcio安裝。 - gRPC-tools:使用命令
python -m pip install grpcio-tools安裝。 - Lanarky及其依賴項:使用命令
python -m pip install "lanarky[openai]"==0.8.6 uvicorn==0.29.0安裝。注意,這與Python 3.12不相容,請使用Python 3.11代替。
無伺服器架構模式
無伺服器架構是一種雲端運算執行模型,雲端提供商負責管理伺服器,使用者只需關心應用程式的開發和佈署。
事件溯源模式
事件溯源是一種架構設計模式,透過儲存應用程式狀態變更的歷史事件來實作狀態管理。
結語
在本章中,我們初步探索了架構設計模式,包括MVC、微服務、無伺服器和事件溯源等。這些模式為開發可擴充套件、可維護和可重複使用的系統提供了基礎。在接下來的章節中,我們將更深入地探討這些架構設計模式,並探索其他相關的主題,如平行和非同步模式、效能最佳化模式、分散式系統模式、測試模式和Python反模式等。