template

第二十四章 模板模式(Template Pattern)

1 概要

模板模式(Template Pattern)是一种行为模式(Behavior Design Patters)。它用于规范和定义一个操作流程。但是,在该流程中,某些步骤的细节交由子类去实现。模板模式是一种流行的模式,因为它的处理逻辑非常适用于一些业务逻辑(Business Logic)的处理。例如,当我们需要从一门考试中选择分数最高的三名学生时,我们可以制定一个流程。这个流程可分为两个步骤:第一个步骤是将所有学生按照成绩排序;第二个步骤是从中选择三名获得最高分数的学生。在这个流程中,第一个步骤可以使用任意一种排序算法(冒泡排序或者快速排序)来完成程序排序。那么,排序算法可以交由子类去实现。这样的例子还有很多,例如:给文件加密。开发人员可以将具体的加密算法(DES(Data Encryption Standard)或者AES(Advanced Encryption Standard))交由子类去实现。

2 模板模式的结构

在模板模式中有着3个参与方。

  1. AbstractProcess定义了整个处理流程以及预留了其中的几个步骤,交由子类实现。
  2. ConcreteProcess继承自AbstractProcess,并且实现了所有的步骤。
  3. Client则调用AbstractProcess接口,完成业务流程的处理。

图一 模板模式结构

图一 模板模式结构。

3 模板模式示例

在本小节中,我们将使用一个小汽车的制作过程为例,介绍模板模式的使用方法。在本例中,我们将模拟本田汽车(Honda)和福特汽车(Ford)的制造过程。我们首先定义类Car,表示小汽车。

public class Car {
}

抽象类CarMaker表示的是小汽车制造商(例如本田汽车或者福特汽车)。CarMaker定义了一个公有成员方法makeCar()。它接收一个Car对象,并在该对象上进行一系列的安装步骤,最终完成一辆小汽车的制作流程。在本例中,我们假设这个流程包括安装车框架(prepareBody),安装汽车引擎(installEngine)和安装窗户(installWindows)。

所以,在makeCar()方法中,设置固化了这个处理流程。但是,其中每个步骤都是抽象方法,需要在子类中实现其具体功能。

public abstract class CarMaker {
    public void makeCar(Car car) {
        prepareBody(car);
        installEngine(car);
        installWindows(car);
        ...  // 省略了其他步骤
    }
    
    protected abstract void prepareBody(Car car);
    protected abstract void installEngine(Car car);
    protected abstract void installWindows(Car car);
}

HondaCarMaker继承自CarMaker,表示的是本田汽车制造商。它能够按照CarMaker制定的流程,生产出一辆本田小汽车。所以,它只需要实现prepareBody()、installEngine()和installWindows()三个方法即可。

public class HondaCarMaker extends CarMaker {
    @Override
    protected void prepareBody(Car car) {
        System.out.println("Preparing the body of a Honda car.");
    }
    
    @Override
    protected void installEngine(Car car) {
        System.out.println("Installing an engine to a Honda car.");
    }
    
    @Override
    protected void installWindows(Car car) {
        System.out.println("Installing windows to a Honda car.");
    }
}

FordCarMaker继承自CarMaker,表示的是福特汽车制造商。它也实现了prepareBody()、installEngine()和installWindows()三个方法,制造一辆福特小汽车。

public class FordCarMaker extends CarMaker {
    @Override
    protected void prepareBody(Car car) {
        System.out.println("Preparing the body of a Ford car.");
    }
    
    @Override
    protected void installEngine(Car car) {
        System.out.println("Installing an engine to a Ford car.");
    }
    
    @Override
    protected void installWindows(Car car) {
        System.out.println("Installing windows to a Ford car.");
    }
}

在使用时,开发人员只需调用makeCar()方法,就能生产出相应品牌的小汽车。

public class CarMakerExample {
    public static void main(String[] args) {
        Car car = new Car();
        CarMaker carMaker = new HondaCarMaker();
        carMaker.makeCar(car);
        ...
    }
}

4 应用举例

模板模式被大量应用于Java标准库中。例如:java.io包中的InputStream和OutputStream,java.util包中的AbstractList、AbstractSet和AbstractMap。在Web应用中,javax.servlet.http.HttpServlet的doGet()/doPost()等方法都是模板方法的应用。

在这里,我们简要的讲解一下模板方法是如何实现java.util.AbstractList中的addAll()方法的。addAll()方法是将一个集合中的所有元素加入到当前的链表中。AbstractList是一个抽象类,它实现了addAll()方法。但是,AbstractList不能实例化。

当开发人员需要一个不能修改的链表类时(Unmodified List),AbstractList已经提供好了add()和addAll()方法。当其中任意一个add()方法被调用时,会抛出UnsupportedOperationException异常。当开发人员需要一个可以修改的链表时,在子类中需要实现并覆盖add()方法。

AbstractList::addAll()方法是一个模板方法的应用。AbstractList类在addAll()方法中制定了一个流程,这个流程包括两个步骤。第一个步骤是检验第一个参数index。当index合法时,才进行第二个步骤。在第二个步骤中,给定集合中的元素会依次添加到当前的链表中。这个添加的方法add()需要在子类中实现。

package java.util;

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    ...
    public boolean add(E e) {
        add(size(), e);
        return true;
    }

    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public boolean addAll(int index, Collection<? extends E> c) {
        // addAll() 方法制定了一个添加元素的流程:先校验index的值,再添加集合中的元素。
        rangeCheckForAdd(index);
        boolean modified = false;
        for (E e : c) {
            add(index++, e); // 需要在子类中实现add()方法
            modified = true;
        }
        return modified;
    }
    ...
}

5 小结

本章介绍了模板模式的结构和使用方法。模板模式非常适合用于完成一个已固化的流程。然而,在这个流程中可能存在可变化的步骤。这些可变化的步骤可在子类中实现。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.