模板方法模式
定義了一套算法的骨架,講某些具體的步驟延遲到子類中實現。 主要用于不改變算法結構的情況下重新定義算法的某些步驟,以適應新的需求。
模板方法的角色
- 抽象類: 作為算法的骨架,該抽象類中包含了算法的核心部分和一些抽象方法,也可能包含一些具體方法,其中抽象方法表示算法中需要子類實現的具體步驟
- 具體子類:繼承抽象類,實現抽象方法以完成算法的具體步驟。 子類也可以實現一些 鉤子方法 以影響算法的執行。
模板方法的優缺點
優點:
- 提高代碼的復用性
- 提高代碼的可擴展性
- 符合開閉原則
缺點:
- 抽象類和具體子類之間耦合度較高(組合優于繼承),抽象類發生變化時,可能會影響到所有子類,比如父類增加新的抽象方法,那么所有的子類需要都實現一遍。
應用場景
- 自上而下的父類視角:在抽象類中實現一個算法的公共部分,將可變的部分 留給具體子類實現
- 自下而上的子類視角:將子類中的公共部分提取出來,集中到父類中,避免代碼重復
示例場景: 文檔處理器
假設我們有一個文檔處理器,它需要執行以下步驟:
打開文檔
解析文檔內容
處理文檔內容
保存處理后的文檔
from abc import ABC, abstractmethod# 抽象類:文檔處理器
class DocumentProcessor(ABC):def process_document(self):"""模板方法,定義處理文檔的步驟"""self.open_document()content = self.parse_document()processed_content = self.handle_content(content)self.save_document(processed_content)@abstractmethoddef open_document(self):"""抽象方法:打開文檔"""pass@abstractmethoddef parse_document(self):"""抽象方法:解析文檔內容"""passdef handle_content(self, content):"""默認實現:處理文檔內容"""# 默認處理邏輯,子類可以重寫return content.upper()@abstractmethoddef save_document(self, processed_content):"""抽象方法:保存處理后的文檔"""pass# 具體類:PDF文檔處理器
class PDFProcessor(DocumentProcessor):def open_document(self):print("打開PDF文檔")def parse_document(self):print("解析PDF內容")return "PDF內容"def save_document(self, processed_content):print(f"保存處理后的PDF內容:{processed_content}")# 具體類:Word文檔處理器
class WordProcessor(DocumentProcessor):def open_document(self):print("打開Word文檔")def parse_document(self):print("解析Word內容")return "Word內容"def save_document(self, processed_content):print(f"保存處理后的Word內容:{processed_content}")def handle_content(self, content):# 重寫處理內容的方法return content.lower()# 測試代碼
def main():# 創建PDF處理器實例pdf_processor = PDFProcessor()pdf_processor.process_document()print("\n")# 創建Word處理器實例word_processor = WordProcessor()word_processor.process_document()if __name__ == "__main__":main()
虛構故事
接-上一節講的狀態模式之比特咖啡故事。比特咖啡的故事-傳送門, 老板姬比特要給比特咖啡研發兩款數字咖啡產品并通過這兩款引流想打造成爆款(美式和卡布奇諾), 程序員-幸瑞接過了這個活,他使用-模板方法模式設計了數字咖啡的制作流程。
且看他的代碼是如何實現的。
from abc import ABC, abstractmethodclass CoffeeMaker(ABC):def make_coffee(self):"""模板方法,定義制作咖啡的整體流程"""self.grind_coffee()self.brew_coffee()self.add_ingredients()self.finish_coffee()@abstractmethoddef grind_coffee(self):"""抽象方法:研磨咖啡豆"""pass@abstractmethoddef brew_coffee(self):"""抽象方法:沖泡咖啡"""passdef add_ingredients(self):"""默認方法:添加配料"""# 默認不添加任何配料,子類可以重寫pass@abstractmethoddef finish_coffee(self):"""抽象方法:完成咖啡制作"""pass
代碼寫到這,骨架算是搭建起來了,幸瑞準備去外面抽根煙休息一會,剛要鎖屏起身時,老板-姬比特走了過來看到屏幕一堆pass,一萬個問號, 老幸,你搞這么多pass干什么, pass在python中是占位符表示什么也不做,我還是懂一點的,你小子是不是急著開溜摸魚啊。
幸瑞連忙給老板解釋說,我這是把數字咖啡的制作流程先抽象了出來,具體的實現方式還沒寫呢,辦公室這會有點悶,我先出去透透氣,順便梳理下思路。 隨后,幸瑞按下了win+L瀟灑的離去。 只剩下老板愣在原地表情瞬間凝固。
20min later…
幸瑞緩緩的走進辦公室開始干活,見他飛快的敲擊著新買的Filco機械鍵盤,聲音響徹狹小的辦公室。 不一會兒又安靜了下來。 原來功能已經實現了,接下來我們看看他是如何實現的。
首先,他創建兩個具體的子類 AmericanoMaker 和 CappuccinoMaker,分別對應美式咖啡和卡布奇諾的制作步驟。
class AmericanoMaker(CoffeeMaker):def grind_coffee(self):print("研磨中度烘焙的咖啡豆")def brew_coffee(self):print("用熱水沖泡咖啡,制作美式咖啡")def add_ingredients(self):print("加入適量的熱水")def finish_coffee(self):print("美式咖啡制作完成,可以享用了!")class CappuccinoMaker(CoffeeMaker):def grind_coffee(self):print("研磨深度烘焙的咖啡豆")def brew_coffee(self):print("用蒸汽牛奶沖泡咖啡,制作卡布奇諾")def add_ingredients(self):print("打發牛奶并加入奶泡")def finish_coffee(self):print("卡布奇諾制作完成,可以享用了!")# 接著,為了驗證設計是否正確,編寫了測試代碼,分別創建美式咖啡和卡布奇諾的制作實例,并調用 make_coffee 方法
def main():# 創建美式咖啡制作實例americano_maker = AmericanoMaker()print("制作美式咖啡:")americano_maker.make_coffee()print("\n")# 創建卡布奇諾制作實例cappuccino_maker = CappuccinoMaker()print("制作卡布奇諾:")cappuccino_maker.make_coffee()if __name__ == "__main__":main()# 運行結果如下
制作美式咖啡:
研磨中度烘焙的咖啡豆
用熱水沖泡咖啡,制作美式咖啡
加入適量的熱水
美式咖啡制作完成,可以享用了!制作卡布奇諾:
研磨深度烘焙的咖啡豆
用蒸汽牛奶沖泡咖啡,制作卡布奇諾
打發牛奶并加入奶泡
卡布奇諾制作完成,可以享用了!
老板,你的數字咖啡制作好了,請享用! 幸瑞大聲的說道,辦公室的N多雙眼睛瞬間將焦點轉移到了幸瑞的工位上…
欲知后事如何,點贊關注不迷路,比特咖啡的故事還會繼續講下去.