内容简介:首先先介绍一下咖啡和茶的冲泡方法:用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设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Design Handbook
Baeck, Philippe de 编 / 2009-12 / $ 22.54
This non-technical book brings together contemporary web design's latest and most original creative examples in the areas of services, media, blogs, contacts, links and jobs. It also traces the latest......一起来看看 《Web Design Handbook》 这本书的介绍吧!