架构师内功心法,职场工作中流程标准化的模板模式详解

210 阅读3分钟

模板模式通常又叫模板方法模式(Template Method Pattern),是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。

一、模板方法的应用场景

模板方法适用于以下应用场景:

  • 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
  • 各子类中的公共行为被提取出来并集中到一个公共的父类中,从而避免代码重复。

现实生活中的模板方法应用场景有填写入职登记表、炒菜流程、大象放进冰箱等

下面以公司的技术培训流程为例:

发布培训通知-->制作培训PPT-->组织员工培训-->现场(远程)培训-->提交培训问卷。我们来创建技术培训 TechnicalTraining 类:

public abstract class TechnicalTraining {

    protected final void createTraining() {

        /**
         * 发布培训通知
         */
        this.PostNotice();
        /**
         * 制作培训PPT
         */
        this.createPPT();
        /**
         * 组织员工培训
         */
        this.OrganizeTraining();

        /**
         * 如果有远程培训的话,就支持远程培训
         */
        if(needOnline()) {
            online();
        }

        /**
         * 现场(远程)培训
         */

        this.training();
        /**
         * 提交培训问卷
         */
        this.sumitQuestionnaire();

    }

    final void PostNotice() {
        System.out.println("发布培训通知");
    }

    final void createPPT() {
        System.out.println("制作培训PPT");
    }

    final void OrganizeTraining() {
        System.out.println("组织员工培训");
    }

    final void training() {
        System.out.println("现场(远程)培训");
    }

    final void sumitQuestionnaire() {
        System.out.println("提交培训问卷");
    }

    //在线培训
    abstract void online();

    protected boolean needOnline() {
        return false;
    }

}

上面的代码中有个boolean值的判断,这个是使用的钩子方法,主要目的是用来干预执行流程,使得我们控制行为流程更加灵活,更符合实际业务的要求。 钩子方法的返回值一般为适合条件分支语句的返回值(如boolean、int等)。可以根据自己的业务场景来决定是否需要使用钩子方法。

创建Java培训的类:

public class JavaTraining extends TechnicalTraining {
    @Override
    void online() {
        System.out.println("远程培训java相关技术");
    }
}

创建数据库培训的类:

public class DatabaseTraining extends TechnicalTraining {

    private boolean needOnlineFlag = false;

    public DatabaseTraining(boolean needOnlineFlag) {
        this.needOnlineFlag = needOnlineFlag;
    }

    @Override
    void online() {
        System.out.println("远程培训数据库相关技术");
    }

    @Override
    protected boolean needOnline() {
        return this.needOnlineFlag;
    }
}

测试代码:

public static void main(String[] args) {

TechnicalTraining javaTraining = new JavaTraining();
javaTraining.createTraining();

TechnicalTraining databaseTraining = new DatabaseTraining(true);
databaseTraining.createTraining();
}

二、模版模式在源码中的体现

2.1 AbstractList类

先看源代码:

/**
 * {@inheritDoc}
 *
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
abstract public E get(int index);

这个get()方法是一个抽象的方法,那么它的逻辑就是交给子类来进行实现,ArrayList就是AbstractList的子类。来看ArrayList中的get()方法的实现代码:

 /**
 * Returns the element at the specified position in this list.
 *
 * @param  index index of the element to return
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

同理,有AbstractList就会有AbstractMap和AbstractSet类。

2.2 HttpServlet类

每天都在用的 HttpServlet,它继承自抽象类GenericServlet,有三个方法service()和init()、destory()方法,都是模板方法的抽象实现。来看下源码:

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;

    public GenericServlet() {
    }

    public void destroy() {
    }

    public String getInitParameter(String name) {
        return this.getServletConfig().getInitParameter(name);
    }

    public Enumeration<String> getInitParameterNames() {
        return this.getServletConfig().getInitParameterNames();
    }

    public ServletConfig getServletConfig() {
        return this.config;
    }

    public ServletContext getServletContext() {
        return this.getServletConfig().getServletContext();
    }

    public String getServletInfo() {
        return "";
    }

    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
    }

    public void log(String msg) {
        this.getServletContext().log(this.getServletName() + ": " + msg);
    }

    public void log(String message, Throwable t) {
        this.getServletContext().log(this.getServletName() + ": " + message, t);
    }

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
}

三、模板方法的优缺点

优点:

  • 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性;

  • 将不同的代码不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性;

  • 把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合开闭原则。

缺点:

  • 类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加;
  • 类数量的增加,间接地增加了系统实现的复杂度;
  • 继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。