设计模式-模板方法模式

167 阅读4分钟

模板模式【常用】

我们再学习另外一种行为型设计模式,模板模式。我们多次强调,绝大部分设计模式的原理和实现,都非常简单,难的是掌握应用场景,搞清楚能解决什么问题。模板模式也不例外。模板模式主要是用来解决复用和扩展两个问题。我们今天会结合 Java Servlet、JUnit TestCase、Java InputStream、Java AbstractList 四个例子来具体讲解这两个作用。

模板模式的原理与实现

  1. 模板模式,全称是模板方法设计模式,英文是 Template Method Design Pattern。在 GoF 的《设计模式》一书中,它是这么定义的:

Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

  1. 翻译成中文就是:模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
  2. 这里的“算法”,我们可以理解为广义上的“业务逻辑”,并不特指数据结构和算法中的“算法”。这里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,这也是模板方法模式名字的由来。
  3. 原理很简单,代码实现就更加简单,我写了一个示例代码,如下所示。templateMethod() 函数定义为 final,是为了避免子类重写它。method1() 和 method2() 定义为 abstract,是为了强迫子类去实现。不过,这些都不是必须的,在实际的项目开发中,模板模式的代码实现比较灵活,待会儿讲到应用场景的时候,我们会有具体的体现。
public abstract class AbstractClass {
    public final void templateMethod() {
        //...
        method1();
        //...
        method2();
        //...
    }

    protected abstract void method1();
    protected abstract void method2();
}

public class ConcreteClass1 extends AbstractClass {

    @Override
    protected void method1() {
        //...
    }

    @Override
    protected void method2() {
        //...
    }
}

public class ConcreteClass2 extends AbstractClass {

    @Override
    protected void method1() {
        //...
    }

    @Override
    protected void method2() {
        //...
    }
}

AbstractClass demo = ConcreteClass1();
demo.templateMethod();

Demo案例-制作豆浆

编写制作豆浆的程序,说明如下:

  1. 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
  2. 通过添加不同的配料,可以制作出不同口味的豆浆
  3. 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
  4. 请使用 模板方法模式 完成 (说明:因为模板方法模式,比较简单,很容易就想到这个方案,因此就直接使用,

不再使用传统的方案来引出模板方法模式

SoyaMilk【抽象类】

//抽象类,表示豆浆
public abstract class SoyaMilk {

   //模板方法, make , 模板方法可以做成final , 不让子类去覆盖.
   final void make() {
      
      select(); 
      if(customerWantCondiments()) {
         addCondiments();
      }
      soak();
      beat();
      
   }
   
   //选材料
   void select() {
      System.out.println("第一步:选择好的新鲜黄豆  ");
   }
   
   //添加不同的配料, 抽象方法, 子类具体实现
   abstract void addCondiments();
   
   //浸泡
   void soak() {
      System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 ");
   }
    
   void beat() {
      System.out.println("第四步:黄豆和配料放到豆浆机去打碎  ");
   }
   
   //钩子方法,决定是否需要添加配料
   boolean customerWantCondiments() {
      return true;
   }
}

RedBeanSoyaMilk

public class RedBeanSoyaMilk extends SoyaMilk {

  @Override
  void addCondiments() {
    // TODO Auto-generated method stub
    System.out.println(" 加入上好的红豆 ");
  }
}

PureSoyaMilk

// 纯豆浆
public class PureSoyaMilk extends SoyaMilk {

  @Override
  void addCondiments() {
    // TODO Auto-generated method stub
    // 空实现
  }

  @Override
  boolean customerWantCondiments() {
    // TODO Auto-generated method stub
    return false;
  }
}

PeanutSoyaMilk

public class PeanutSoyaMilk extends SoyaMilk {

  @Override
  void addCondiments() {
    // TODO Auto-generated method stub
    System.out.println(" 加入上好的花生 ");
  }
}

Client

public class Client {

  public static void main(String[] args) {
    System.out.println("----制作红豆豆浆----");
    SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
    redBeanSoyaMilk.make();

    System.out.println("----制作花生豆浆----");
    SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
    peanutSoyaMilk.make();

    System.out.println("----制作纯豆浆----");
    SoyaMilk pureSoyaMilk = new PureSoyaMilk();
    pureSoyaMilk.make();
  }
}

模板模式作用一:复用

开篇的时候,我们讲到模板模式有两大作用:复用和扩展。我们先来看它的第一个作用:复用。模板模式把一个算法中不变的流程抽象到父类的模板方法 templateMethod() 中,将可变的部分 method1()、method2() 留给子类 ContreteClass1 和 ContreteClass2 来实现。所有的子类都可以复用父类中模板方法定义的流程代码。

模板模式作用二:扩展

模板模式的第二大作用的是扩展。这里所说的扩展,并不是指代码的扩展性,而是指框架的扩展性,有点类似我们之前讲到的控制反转。基于这个作用,模板模式常用在框架的开发中,让框架用户可以在不修改框架源码的情况下,定制化框架的功能。