概念

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

该设计模式主要针对这样一种场景:当要做一件事儿的时候,这件事儿的步骤是固定好的,但是每一个步骤的具体实现方式是不一定的。这样,我们可以把所有要做的事儿抽象到一个抽象类中,并在该类中定义一个模板方法。

举例

去银行的营业厅办理业务需要以下步骤:1.取号、2.办业务、3.评价。三个步骤中取号和评价都是固定的流程,每个人要做的事儿都是一样的。但是办业务这个步骤根据每个人要办的事情不同所以需要有不同的实现。我们可以将整个办业务这件事儿封装成一个抽象类:

  1. /**
  2. * 模板方法设计模式的抽象类
  3. * @author hollis
  4. */
  5. public abstract class AbstractBusinessHandeler {
  6. /**
  7. * 模板方法
  8. */
  9. public final void execute(){
  10. getRowNumber();
  11. handle();
  12. judge();
  13. }
  14. /**
  15. * 取号
  16. * @return
  17. */
  18. private void getRowNumber(){
  19. System.out.println("rowNumber-00" + RandomUtils.nextInt());
  20. }
  21. /**
  22. * 办理业务
  23. */
  24. public abstract void handle(); //抽象的办理业务方法,由子类实现
  25. /**
  26. * 评价
  27. */
  28. private void judge(){
  29. System.out.println("give a praised");
  30. }
  31. }

该类中定义了四个方法,其中getRowNumberjudge这两个方法是私有的非抽象方法。他们实现了取号和评价的业务逻辑,因为这两部分内容是通用的。还有一个抽象的handle方法,这个方法需要子类去重写,根据办理业务的具体内容重写该方法。还有一个模板方法就是final类型的execute方法,他定义好了需要做的事儿和做这些事儿的顺序。
现在,有了这个抽象类和方法,如果有人想要办理业务,那么只需要继承该AbstractBusinessHandeler并且重写handle方法,然后再使用该实现类的对象调用execute方法,即可完成整个办理业务的流程。

  1. public class SaveMoneyHandler extends AbstractBusinessHandeler {
  2. @Override
  3. public void handle() {
  4. System.out.println("save 1000");
  5. }
  6. public static void main(String []args){
  7. SaveMoneyHandler saveMoneyHandler = new SaveMoneyHandler();
  8. saveMoneyHandler.execute();
  9. }
  10. }//output:编号:rowNumber-001 save 1000 give a praised

模板方法模式是结构最简单的行为型设计模式,在其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。模板方法模式提供了一个模板方法来定义算法框架,而某些具体步骤的实现可以在其子类中完成。

钩子方法

当在模板方法中某一些步骤是可选的时候,也就是该步骤不一定要执行,可以由子类来决定是否要执行,则此时就需要用上钩子。钩子是一种被声明在抽象类中的方法,但一般来说它只是空的或者具有默认值,子类可以实现覆盖该钩子,来设置算法步骤的某一步骤是否要执行。钩子可以让子类实现算法中可选的部分,让子类能够有机会对模板方法中某些一即将发生的步骤做出反应。
还是办理业务的例子,如果来的客户是vip客户,那么他就可以不必取号,可以直接办理业务。 修改抽象类如下:

  1. public abstract class AbstractBusinessHandeler {
  2. public final void execute(){
  3. if(!isVip()){//如果顾客是vip,则不用排队
  4. getRowNumber();
  5. }
  6. handle();
  7. judge();
  8. }
  9. public abstract boolean isVip();//抽象的钩子方法,由子类实现
  10. private void getRowNumber(){
  11. System.out.println("rowNumber-00" + RandomUtils.nextInt());
  12. }
  13. public abstract void handle();
  14. private void judge(){
  15. System.out.println("give a praised");
  16. }
  17. }

那么,他的实现类就可以根据具体情况来复写其中的方法,然后同样调用execute方法。

总结

1.模板方法模式是一种类的行为型模式,在它的结构图中只有类之间的继承关系,没有对象关联关系。
2.模板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一。在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中。
3.在模板方法模式中,我们需要准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来让子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。模板方法模式体现了面向对象的诸多重要思想,是一种使用频率较高的模式。
4.钩子是一种方法,它在抽象类中不做事,或者是默认的事情,子类可以选择覆盖它
5.为了防止子类改变模板方法中的算法骨架,一般将模板方法声明为final
6.策略模式和模板方法都是用于封装算法,前者是利用组合和委托模型,而后者则是继承