python设计模式-模板方法模式
date: 2018-12-02T17:23:56+08:00
description: python 设计模式 模板方法模式
draft: false
slug: "python-design-pattern-template-pattern"
categories: ["development", "python", "设计模式"]
tags: ["python", "读书笔记", "设计模式"]
title: "python设计模式-模板方法模式"
首先先介绍一下咖啡和茶的冲泡方法:
茶
1. 把水煮沸 2. 用沸水浸泡茶叶 3. 把茶放到杯子里
咖啡
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
这三个方法。
问题:
如何重新设计这两个类来让代码更简洁呢?
首先看一下两个类的类图:
- 每个类中都有
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()
也抽象化?
对比 Tea
和 Coffee
的prepare_recipe()
方法会发现,他们之间的差异主要是:
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)。