专栏首页程序员小跃设计模式之模板方法模式(一)

设计模式之模板方法模式(一)

学习了前面的朋友都知道,目前为止,我们的议题都是绕着封装转;我们已经封装了对象创建、方法调用、复杂接口、鸭子、比萨…那接下来呢

我们将要深入封装算法块、好让子类可以在任何时候都可以将自己挂接进运算里。我们甚至会在这里学到一个受好莱坞影响而启发的设计原则。

喝点咖啡或茶饮

有些人喜欢喝咖啡,没有咖啡感觉生活索然无趣;有些人喜欢喝茶。那么,同样是茶饮,两者有没有什么共同或者是啥的?其实,两者的冲泡方式非常相似。比如

  1. 咖啡冲泡法
  • (1)把水煮沸
  • (2)用沸水冲泡咖啡
  • (3)把咖啡倒进杯子
  • (4)加糖和牛奶
  1. 茶饮冲泡法
  • (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. 把水煮沸
  2. 用热水泡咖啡或茶
  3. 把饮料倒进杯子
  4. 在饮料内加入适当的调料

可以看到,1和4已经被抽出来,放到了基类中。2和3并没有抽出来,但它们本质还是一样的,只是应用在不同的饮料上罢了。

那么,我们有办法将prepareRecipe()也抽象化吗?是的,我们可以哦

抽象prepareRecipe()

  1. 我们所遇到的第一个问题,就是咖啡使用brewCoffeeGrinds()和addSuagrAndMilk()方法,而茶使用steepTeaBag()和addLemon().

我们发现,steep和brew都是泡的动作;addSugarAndMilk和addLemon都是加调料,所以分别把他们统一成brew()和addCondiments()进行

  1. 现在我们有了新的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");
    }
}
  1. 最后我们需要处理咖啡和茶类了。这两个类现在都依赖超类(咖啡因饮料)来处理冲泡法,所以只需要自行处理冲泡和添加调料部分即可:
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()就是我们的模板方法。为什么呢?

  • 毕竟它是一个方法
  • 它用作一个算法的模板,在这个例子中,算法是用来制作咖啡和茶饮的

模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现

让我们泡茶喝吧

让我们逐步地泡茶、追踪这个模板方法是如何工作的。你会得知在算法内的某些方法,该模板方法控制了算法。它让子类能够提供某些步骤的实现

  1. 首先我们需要一个茶对象
Tea myTea = new Tea();
  1. 然后我们调用这个模板方法
myTea.prepareRecipe();
  1. 把水煮沸
boilWater();

这件事情是在超类中进行的
  1. 接下来我们需要泡茶,这件事情只有子类才能知道要怎么做
brew();
  1. 现在把茶倒进杯子中;所有的饮料做法都一样,所以这件事情发生在超类中
pourInCup();
  1. 最后,我们加进调料,由于调料是各个饮料独有的,所以 由子类来实现它
addCondiments();

经过上述的流程,我们就初步模板方法模式给学会了。在今天的篇尾,我们定义下这个模板方法模式:

模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

这个模式是有从来创建一个算法的模块。什么是模块?如你所见,模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。

好啦,今天的学习就先到这里。今天只是初步学习了模板方法模式,接下来还会有更有料的方式,下次不见不散。

爱生活,爱学习,爱感悟,爱挨踢

本文分享自微信公众号 - 奔跑吧攻城狮(runningdimple),作者:Dimple

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计模式之状态模式(一)

    我原本以为在对乡村的一切事物都很容易,但是每次我一回头就有更多变更的请求纷至沓来。我快崩溃了!----当小编读到这段话的时候,甚是感慨,设计模式的世界真的不是那...

    程序员小跃
  • 设计模式之状态模式(二)

    在上一次的文章里,我们看到,需求的变更,迫使我们需要重新改造现有的糖果机代码来符合这个新提的需求。但是,也并没有难倒我们,至少我们在文末给出了思路和类图,不知道...

    程序员小跃
  • Dimple在左耳听风ARTS打卡(十四)

    所谓ARTS:每周至少做一个LeetCode的算法题;阅读并点评至少一篇英文技术文章;学习至少一个技术技巧;分享一篇有观点和思考的技术文章。(也就是Algori...

    程序员小跃
  • 09(01)总结final,多态,抽象类,接口

    1:final关键字(掌握) (1)是最终的意思,可以修饰类,方法,变量。 (2)特点: A:它修饰的类,不能被继承。 B:它修饰的方法,不能被重写。 ...

    Java帮帮
  • Java设计模式之桥梁模式

    梦想中的我自己,身价过亿,有两个大公司,一个是房地产公司,一个是服装制造公司,其实是什么公司我倒是不关心,我关心的是它赚不赚钱,赚了多少,这才是我关心的。先用类...

    CoderJed
  • TestNG用法概括

    应用场景:将通用的功能抽取并封装起来,在方法中使用注解@BeforeSuite和@AfterSuite,在测试类中继承这个类,测试类在运行前会先运行@Befor...

    桑鱼
  • SpringMVC快速使用AOP

    最近突然想到了AOP编程,于是在自己的SpringMVC小项目中引入了。AOP一般用来做些什么呢?这里简单的使用AOP做拦截日志,来介绍下在SpringMVC中...

    幽鸿
  • Spring Boot 2.X(十):自定义注册 Servlet、Filter、Listener

    在 Spring Boot 中已经移除了 web.xml 文件,如果需要注册添加 Servlet、Filter、Listener 为 Spring Bean,在...

    朝雾轻寒
  • Spring Boot 2.X(十):自定义注册 Servlet、Filter、Listener

    在 Spring Boot 中已经移除了 web.xml 文件,如果需要注册添加 Servlet、Filter、Listener 为 Spring Bean,在...

    朝雾轻寒
  • java设计模式(7)-外观模式

    上篇文章分享代理模式,这篇整理外观模式,外观模式可以降低类与类之间的耦合程度,减少维护工作量

    爱敲代码的猫

扫码关注云+社区

领取腾讯云代金券