首先先介紹一下咖啡和茶的沖泡方法:python
茶git
1. 把水煮沸
2. 用沸水浸泡茶葉
3. 把茶放到杯子裏
複製代碼
咖啡github
1. 把水煮沸
2. 用沸水沖泡咖啡
3. 把咖啡倒進杯子
4. 加糖和牛奶
複製代碼
用python代碼實現沖泡方法大概是這個樣子:算法
# 茶的製做方法
class Tea:
def prepare_recipe(self):
# 在下邊實現具體步驟
self.boil_water()
self.brew_tea_bag()
self.pour_in_cup()
def boil_water(self):
print("Boiling water")
def brew_tea_bag(self):
print("Steeping the tea")
def pour_in_cup(self):
print("Pouring into cup")
複製代碼
# 咖啡的製做方法
class Coffee:
def prepare_recipe(self):
# 在下邊實現具體步驟
self.boil_water()
self.brew_coffee_grinds()
self.pour_in_cup()
self.add_sugar_and_milk()
def boil_water(self):
print("Boiling water")
def brew_coffee_grinds(self):
print("Dripping Coffee through filter")
def pour_in_cup(self):
print("Pouring into cup")
def add_sugar_and_milk(self):
print("Adding Sugar and Milk")
複製代碼
仔細看上邊兩端代碼會發現,茶和咖啡的實現方式基本相似,都有prepare_recipe
,boil_water
,pour_in_cup
這三個方法。小程序
問題:
如何從新設計這兩個類來讓代碼更簡潔呢?設計模式
首先看一下兩個類的類圖:bash
prepare_recipe() boil_water() pour_in_cup()
方法。prepare_recipe()
方法的實現都不同。如今把prepare_recipe() boil_water() pour_in_cup()
三個方法抽取出來作成一個父類CoffeineBeverage()
,Tea
和 Coffee
都繼自CoffeineBeverage()
。併發
由於每一個類中
prepare_recipe()
實現的方法不同,因此Tea
和Coffee
類都分別實現了prepare_recipe()
。問題
: 那麼,有沒有辦法將prepare_recipe()
也抽象化?post
對比 Tea
和 Coffee
的prepare_recipe()
方法會發現,他們之間的差別主要是:spa
def prepare_recipe(self):
# 相同部分隱藏
# self.boil_water()
self.brew_tea_bag() # 差別1
#self.pour_in_cup()
def prepare_recipe(self):
# 相同部分隱藏
# self.boil_water()
self.brew_coffee_grinds() # 差別1
# self.pour_in_cup()
self.add_sugar_and_milk() # 差別2
複製代碼
這裏的實現思路是,將兩處差別分別用新的方法名代替,替換後結果以下:
def prepare_recipe(self):
# 新的實現方法
self.boil_water()
self.brew() # 差別1 使用brew 代替 brew_tea_bag 和 brew_coffee_grinds
self.pour_in_cup()
self.add_condiments() # 差別2 Tea 不須要此方法,能夠用空的實現代替
複製代碼
新的類圖以下:
如今,類 Tea
和 Coffee
只須要實現具體的 brew()
和 add_condiments()
方法便可。代碼實現以下:
class CoffeineBeverage:
def prepare_recipe(self):
# 新的實現方法
self.boil_water()
self.brew()
self.pour_in_cup()
self.add_condiments()
def boil_water(self):
print("Boiling water")
def brew(self):
# 須要在子類實現
raise NotImplementedError
def pour_in_cup(self):
print("Pouring into cup")
def add_condiments(self):
# 這裏實際上是個鉤子方法,子類能夠視狀況選擇是否覆蓋
# 鉤子方法是一個可選方法,也可讓鉤子方法做爲某些條件觸發後的動做
pass
# 茶的製做方法
class Tea(CoffeineBeverage):
def brew(self):
# 父類中聲明瞭 raise NotImplementedError,這裏必需要實現此方法
print("Steeping the tea")
# Tea 不須要 add_condiments 方法,因此這裏不須要實現
# 咖啡的製做方法
class Coffee(CoffeineBeverage):
def brew(self):
# 父類中聲明瞭 raise NotImplementedError,這裏必需要實現此方法
print("Dripping Coffee through filter")
def add_condiments(self):
print("Adding Sugar and Milk")
複製代碼
上述抽象過程使用的就是模板方法。模板方法定義了一個算法的步驟,而且容許子類爲一個或多個步驟提供實現。在這個例子中,prepare_recipe
就是一個模板方法。
定義:
模板方法牧師在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠在不改變算法結構的狀況下,從新定義算法中的某些步驟。
模板方法使用到了一個原則,好萊塢原則
。
好萊塢原則
,別調用我,我會調用你。
在上邊的例子中,CoffeineBeverage 是高層組件,Coffee和Tea 是低層組件,他們不會之間調用抽象類(CoffeineBeverage)。
Python 第三方表單驗證包 wtforms 的表單驗證部分就使用到了模板方法模式。Field 類中validate
方法就是一個模板方法,在這個方法中,會調用 pre_validate
, _run_validation_chain
,post_validate
方法來驗證表單,這些方法也均可以在子類中從新實現。具體實現能夠參考如下源碼。
本文例子來自《Head First 設計模式》
最後,感謝女友支持和包容,比❤️
也能夠在公號輸入如下關鍵字獲取歷史文章:公號&小程序
| 設計模式
| 併發&協程