内容简介:首先先介绍一下咖啡和茶的冲泡方法:用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)。
一个例子:chestnut:
Python 第三方表单验证包 wtforms 的表单验证部分就使用到了模板方法模式。Field 类中 validate
方法就是一个模板方法,在这个方法中,会调用 pre_validate
, _run_validation_chain
, post_validate
方法来验证表单,这些方法也都可以在子类中重新实现。具体实现可以参考以下源码。
源码地址: github.com/wtforms/wtf…
参考链接
本文例子来自《Head First 设计模式》
最后,感谢女朋友支持和包容,比:heart:
也可以在公号输入以下关键字获取历史文章: 公号&小程序
| 设计模式
| 并发&协程
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 设计模式——订阅模式(观察者模式)
- 设计模式-简单工厂、工厂方法模式、抽象工厂模式
- java23种设计模式-门面模式(外观模式)
- 设计模式-享元设计模式
- Java 设计模式之工厂方法模式与抽象工厂模式
- JAVA设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。