模板方法模式(Template Method Pattern) 实际上是封装了一个固定流程,该流程由几个步骤组成,具体步骤可以由子类进行不同实现,从而让固定的流程产生不同的结果。
模板方法模式 非常简单,其实就是类的继承机制,但它却是一个应用非常广泛的模式。
模板方法模式 本质:抽象封装流程,具体进行实现

主要解决

当完成一个操作具有固定的流程时,由抽象固定流程步骤,具体步骤交给子类进行具体实现(固定的流程,不同的实现)。

优缺点

优点

  • 封装不变,扩展可变:父类封装了具体流程以及实现部分不变行为,其它可变行为交由子类进行具体实现;
  • 流程由父类控制,子类进行实现:框架流程由父类限定,子类无法更改;子类可以针对流程某些步骤进行具体实现;

缺点

  • 抽象规定了行为,具体负责实现,与通常事物的行为相反,会带来理解上的困难(通俗地说,“父类调用了子类方法”);

    使用场景

  • 多个子类有公有的方法,并且逻辑基本相同时;

  • 重要,复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现;
  • 重构时,模板方法模式 是一个经常使用的模式,把相同的代码抽取到父类,然后通过钩子函数约束其行为;

    模式讲解

    首先来看下 模板方法模式 的通用 UML 类图:
    模板方法模式 - 图1
    模板方法模式
    从 UML 类图中,我们可以看到,模板方法模式 主要包含两种角色:

  • 抽象模板(AbstractClass):抽象模板类,定义了一套算法框架/流程;

  • 具体实现(ConcreteClass):具体实现类,对算法框架/流程的某些步骤进行了实现;

以下是 模板方法模式 的通用代码:

  1. class Client {
  2. public static void main(String[] args) {
  3. AbstractClass abc = new ConcreteClassA();
  4. abc.templateMehthod();
  5. abc = new ConcreteClassB();
  6. abc.templateMehthod();
  7. }
  8. // 抽象模板类
  9. static abstract class AbstractClass {
  10. protected void step1() {
  11. System.out.println("AbstractClass:step1");
  12. }
  13. protected void step2() {
  14. System.out.println("AbstractClass:step2");
  15. }
  16. protected void step3() {
  17. System.out.println("AbstractClass:step3");
  18. }
  19. // 声明为final方法,避免子类覆写
  20. public final void templateMehthod() {
  21. this.step1();
  22. this.step2();
  23. this.step3();
  24. }
  25. }
  26. // 具体实现类A
  27. static class ConcreteClassA extends AbstractClass {
  28. @Override
  29. protected void step1() {
  30. System.out.println("ConcreateClassA:step1");
  31. }
  32. }
  33. // 具体实现类B
  34. static class ConcreteClassB extends AbstractClass {
  35. @Override
  36. protected void step2() {
  37. System.out.println("ConcreateClassB:step2");
  38. }
  39. }
  40. }

:通常把抽象模板类AbstractClass的模板方法templateMethod定义成final类型,避免子类对其覆写,并遵命定义算法结构/流程的语义。

举个例子

例子:小明要炒两道菜:炒豆芽和炒茄子;
分析:炒菜都有固定步骤:洗菜,热锅下油,下菜翻炒,下调料,起锅。由于炒菜流程是固定的,而其中有些步骤对不同的菜而言具备不同的操作,因此可以很好地使用 模板方法模式 完成炒菜过程。
具体代码如下:

  1. class Client {
  2. public static void main(String[] args) {
  3. System.out.println("准备炒豆芽");
  4. CookVegetable cookVegetable = new CookBeanSprout();
  5. cookVegetable.cook();
  6. System.out.println();
  7. System.out.println("准备炒茄子");
  8. cookVegetable = new CookEggplant();
  9. cookVegetable.cook();
  10. }
  11. // 抽象模板类:定义炒菜流程
  12. static abstract class CookVegetable {
  13. protected void wash() {
  14. System.out.println("洗菜");
  15. }
  16. protected void pourOil() {
  17. System.out.println("热油下锅");
  18. }
  19. protected void fry() {
  20. System.out.println("下菜翻炒");
  21. }
  22. // 具体调料由菜决定
  23. protected abstract void pourSauce();
  24. // 具体炒菜流程
  25. public final void cook() {
  26. this.wash();
  27. this.pourOil();
  28. this.fry();
  29. this.pourSauce();
  30. System.out.println("起锅吃菜");
  31. }
  32. }
  33. // 豆芽
  34. static class CookBeanSprout extends CookVegetable {
  35. @Override
  36. protected void pourOil() {
  37. System.out.println("热锅少油");
  38. }
  39. @Override
  40. protected void fry() {
  41. System.out.println("快速翻炒");
  42. }
  43. @Override
  44. protected void pourSauce() {
  45. System.out.println("加盐和少量生抽");
  46. }
  47. }
  48. // 茄子
  49. static class CookEggplant extends CookVegetable {
  50. @Override
  51. protected void wash() {
  52. System.out.println("去除头尾,然后用水洗下");
  53. }
  54. @Override
  55. protected void pourOil() {
  56. System.out.println("热锅多油");
  57. }
  58. @Override
  59. protected void pourSauce() {
  60. System.out.println("加盐和鸡精");
  61. }
  62. }
  63. }

结果如下:

  1. 准备炒豆芽
  2. 洗菜
  3. 热锅少油
  4. 快速翻炒
  5. 加盐和少量生抽
  6. 起锅吃菜
  7. 准备炒茄子
  8. 去除头尾,然后用水洗下
  9. 热锅多油
  10. 下菜翻炒
  11. 加盐和鸡精

我们根据 模板方法模式,就可以抽象定义炒菜的流程,然后针对不同的菜品,由其子类对某些炒菜步骤进行具体实现,这样就完成了炒菜过程。