学习了前面的朋友都知道, 目前为止, 我们的议题都是绕着封装转; 我们已经封装了对象创建, 方法调用, 复杂接口, 鸭子, 比萨... 那接下来呢?
我们将要深入封装算法块, 好让子类可以在任何时候都可以将自己挂接进运算里. 我们甚至会在这里学到一个受好莱坞影响而启发的设计原则.
喝点咖啡或茶饮
有些人喜欢喝咖啡, 没有咖啡感觉生活索然无趣; 有些人喜欢喝茶. 那么, 同样是茶饮, 两者有没有什么共同或者是啥的? 其实, 两者的冲泡方式非常相似. 比如
咖啡冲泡法
(1)把水煮沸
(2)用沸水冲泡咖啡
(3)把咖啡倒进杯子
(4)加糖和牛奶
茶饮冲泡法
(1)把水煮沸
(2)用沸水浸泡茶叶
(3)把茶倒进杯子
(4)加柠檬
转换成咖啡和茶的代码如下:
- public class Coffee {
- void prepareRecipe() {
- boilWater();
- brewCoffeeGrinds();
- pourInCup();
- addSugarAndMilk();
- }
- public void boilWater() {
- System.out.println("Boling water");
- }
- public void brewCoffeeGrinds() {
- System.out.println("Dripping Coffee through filter");
- }
- public void pourInCup() {
- System.out.print("Pouring into cup");
- }
- public void addSugarAndMilk() {
- System.out.println("Adding Sugar and Milk");
- }
- }
- public class Tea {
- void prepareRecipe() {
- boilWater();
- steepTeaBag();
- pourInCup();
- addLemon();
- }
- public void boilWater() {
- System.out.println("Boiling water");
- }
- public void steepTeaBag() {
- System.out.println("Steeping the tea");
- }
- public void addLemon() {
- System.out.println("Adding Lemon");
- }
- public void pourInCup() {
- System.out.println("Pouring into cup");
- }
- }
茶类也是类似的, 所以就会出现代码又相通的地方, 给我们的提示就是, 第一版的设计类图可以是如下的方式:
更进一步的设计
所以, 查看了以上的代码和类图, 咖啡和茶还有什么其他的共同点呢? 让我们先从冲泡法下手.
把水煮沸
用热水泡咖啡或茶
把饮料倒进杯子
在饮料内加入适当的调料
可以看到, 1 和 4 已经被抽出来, 放到了基类中. 2 和 3 并没有抽出来, 但它们本质还是一样的, 只是应用在不同的饮料上罢了.
那么, 我们有办法将 prepareRecipe()也抽象化吗? 是的, 我们可以哦.
抽象 prepareRecipe()
我们所遇到的第一个问题, 就是咖啡使用 brewCoffeeGrinds()和 addSuagrAndMilk()方法, 而茶使用 steepTeaBag()和 addLemon().
我们发现, steep 和 brew 都是泡的动作; addSugarAndMilk 和 addLemon 都是加调料, 所以分别把他们统一成 brew()和 addCondiments()进行
现在我们有了新的 prepareRecipe()方法, 但是需要让它能够符合代码. 要想这么做, 我们先从 CaffeineBeveage(咖啡因饮料)超类开始
- public abstract class CaffeineBeverage {
- final void prepareRecipe() {
- }
- abstract void brew();
- abstract void addCondiments();
- void boilWater() {
- System.out.println("Boiling water");
- }
- void pourInCup() {
- System.out.println("Pouring into cup");
- }
- }
最后我们需要处理咖啡和茶类了. 这两个类现在都依赖超类 (咖啡因饮料) 来处理冲泡法, 所以只需要自行处理冲泡和添加调料部分即可:
- public class Tea extends CaffeineBeverage {
- public void brew() {
- System.out.println("Steeping the tea");
- }
- public void addCondiments() {
- System.out.println("Adding Lemon");
- }
- }
- public class Coffee extends CaffeineBeverage {
- public void brew() {
- System.out.println("Dripping Coffee through filter");
- }
- public void addCondiments() {
- System.out.println("Adding Sugar and Milk");
- }
- }
那么我们在这个过程中做了什么呢? 小编用书中给出的形象化的图给大家解释下:
认识模板方法
其实, 我们在这个过程中已经使用了我们要学习的模板方法, prepareRecipe()就是我们的模板方法. 为什么呢?
毕竟它是一个方法
它用作一个算法的模板, 在这个例子中, 算法是用来制作咖啡和茶饮的
模板方法定义了一个算法的步骤, 并允许子类为一个或多个步骤提供实现.
让我们泡茶喝吧
让我们逐步地泡茶, 追踪这个模板方法是如何工作的. 你会得知在算法内的某些方法, 该模板方法控制了算法. 它让子类能够提供某些步骤的实现
首先我们需要一个茶对象
Tea myTea = new Tea();
然后我们调用这个模板方法
myTea.prepareRecipe();
把水煮沸
boilWater();
这件事情是在超类中进行的
接下来我们需要泡茶, 这件事情只有子类才能知道要怎么做
brew();
现在把茶倒进杯子中; 所有的饮料做法都一样, 所以这件事情发生在超类中
pourInCup();
最后, 我们加进调料, 由于调料是各个饮料独有的, 所以 由子类来实现它
addCondiments();
经过上述的流程, 我们就初步模板方法模式给学会了. 在今天的篇尾, 我们定义下这个模板方法模式:
模板方法模式: 在一个方法中定义一个算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变算法结构的情况下, 重新定义算法中的某些步骤.
这个模式是有从来创建一个算法的模块. 什么是模块? 如你所见, 模板就是一个方法. 更具体地说, 这个方法将算法定义成一组步骤, 其中的任何步骤都可以是抽象的, 由子类负责实现. 这可以确保算法的结构保持不变, 同时由子类提供部分实现.
好啦, 今天的学习就先到这里. 今天只是初步学习了模板方法模式, 接下来还会有更有料的方式, 下次不见不散.
爱生活, 爱学习, 爱感悟, 爱挨踢
来源: https://www.cnblogs.com/dimple91/p/10973159.html