java设计模式-模板方法模式

1.模板方法(Template Method)模式的定义

  • 它定义一个操作中的算法的框架,而将一些步骤延迟到了子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。它是一种类行为型模式。

2.模板方法模式的优缺点

优点:

  • 良好的封装性。把公有的不变的方法封装在父类,而子类负责实现具体逻辑。
  • 良好的扩展性。增加功能由子类实现基本方法扩展,符合单一职责原则和开闭原则。
  • 它在父类中提取了公共的部分代码,便于复用代码。

缺点:

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

3.模板方法模式主要角色

  3.1 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下:

    3.1.1 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

    3.1.2 基本方法:是整个算法中的一个步骤,包含以下几种类型:

      抽象方法:在抽象类中申明,由具体子类实现。

      具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。

      钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

  3.2 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。

4.模板方法模式的结构图

5.模板方法模式的实现,以房子构建模板为例

  • 定义抽象类:房子构建模板
package com.lw.designpattern.templatemethod;

/**
 * @Classname HouseTemplate
 * @Description 定义抽象类(房子构建模板):
 *  负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
 * @Author lw
 * @Date 2020-01-14 14:18
 */
public abstract class HouseTemplate {

    /** 房子构建模板名称 */
    protected String templateName;

    protected HouseTemplate(String templateName) {
        this.templateName = templateName;
    }

    /** 模板方法则使用protected修饰,表明其需要在子类中实现 */
    /** 构建门 */
    protected abstract void buildDoor();
    /** 构建窗户 */
    protected abstract void buildWindow();
    /** 构建墙 */
    protected abstract void buildWall();
    /** 构建基地 */
    protected abstract void buildBase();
    /** 构建厕所 */
    protected abstract void buildToilet();

    /**
     * 钩子方法:给予授权,判断是否要执行
     *
     * @return boolean
     */
    protected boolean isBuildToilet() {
        return true;
    }

    /**
     * 基本方法:构建房子。用final修饰,保证其不会被子类修改
     */
    public final void buildHouse() {
        buildDoor();
        buildWindow();
        buildWall();
        buildBase();
        if (isBuildToilet()) {
            buildToilet();
        }
    }
}
  • 创建具体子类:房子构建模板one,继承HouseTemplate抽象类
package com.lw.designpattern.templatemethod;

/**
 * @Classname HouseOne
 * @Description 具体子类房子构建模板one:实现抽象类中所定义的抽象方法和钩子方法
 * @Author lw
 * @Date 2020-01-14 14:37
 */
public class HouseOne extends HouseTemplate {

    /** 是否需要构建厕所标志 */
    public boolean isBuildToilet;

    public HouseOne(String templateName) {
        super(templateName);
    }

    public HouseOne(String templateName, boolean isBuildToilet) {
        this(templateName);
        this.isBuildToilet = isBuildToilet;
    }

    @Override
    protected void buildDoor() {
        System.out.println(templateName +"的门要采用防盗门");
    }

    @Override
    protected void buildWindow() {
        System.out.println(templateName + "的窗户要面向北方");
    }

    @Override
    protected void buildWall() {
        System.out.println(templateName + "的墙使用大理石建造");
    }

    @Override
    protected void buildBase() {
        System.out.println(templateName + "的地基使用钢铁地基");
    }

    @Override
    protected void buildToilet() {
        System.out.println(templateName + "的厕所建在东南角");
    }

    @Override
    protected boolean isBuildToilet() {
        return isBuildToilet;
    }
}
  • 再创建一个具体子类:房子构建模板two,继承HouseTemplate抽象类
package com.lw.designpattern.templatemethod;

/**
 * @Classname HouseTwo
 * @Description 具体子类房子构建模板two:实现抽象类中所定义的抽象方法和钩子方法
 * @Author lw
 * @Date 2020-01-14 14:37
 */
public class HouseTwo extends HouseTemplate {

    public HouseTwo(String templateName) {
        super(templateName);
    }

    @Override
    protected void buildDoor() {
        System.out.println(templateName + "的门采用木门");
    }

    @Override
    protected void buildWindow() {
        System.out.println(templateName + "的窗户要向南");
    }

    @Override
    protected void buildWall() {
        System.out.println(templateName + "的墙使用玻璃制造");
    }

    @Override
    protected void buildBase() {
        System.out.println(templateName + "的地基使用花岗岩");
    }

    @Override
    protected void buildToilet() {
        System.out.println(templateName + "的厕所建在西北角");
    }
}
  • 单元测试
  /**
     * 模板方法模式
     */
    @Test
    public void testTemplateMethod() {
        // 创建房子模板对象
        HouseTemplate houseOne = new HouseOne("房子模板one", false);
        HouseTemplate houseTwo = new HouseTwo("房子模板two");
        // 房子构建模板
        System.out.println("=== 房子模板one,构建模板 ===");
        houseOne.buildHouse();
        System.out.println("=== 房子模板two,构建模板 ===");
        houseTwo.buildHouse();
    }

结果打印

通过执行结果我们可以清晰的看到,通过重写钩子方法自定义了房子模板one不需要构建厕所(fasle)。

6.模板方法模式的应用场景

  • 在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中。
  • 程序主框架相同,细节不同的情况下,也可以使用模板方法。
原文地址:https://www.cnblogs.com/lwcode6/p/12191728.html