设计模式-模板方法模式

设计模式 / 2022-04-20

当我们知道一个业务流程或算法的关键步骤,并且也确定了步骤的执行顺序,但是某些步骤的具体实现还不能确定或者有多个方案。这时候就可以利用模板方法设计模式,将某些具体实现还不能确定或者有多个方案的方法,封装为一个抽象方法,子类通过继承父类来实现不同步骤的具体实现。整体的工作流程却由父类进行公职。

举例一

我们来举个例子,我们要创建一个工作流程的类,基本的步骤是上班打卡,执行具体的工作内容,然后下班。但是不同的人工作内容是不同的,但是都会执行上班班和下班的步骤,我们可以把具体执行的工作内容封装为一个抽象方法,交给子类进行实现。来看看代码:

public abstract class AbstractWorkProcess {

    public void work(){
        //打卡
        System.out.println("打卡成功");

        jobContent();
        
        //下班
        System.out.println("下班");
    }

    // 具体的工作内容
    public abstract void jobContent();
}

程序员子类

public class ProgrammerWork extends AbstractWorkProcess{
    @Override
    public void jobContent() {
        System.out.println("敲代码");
    }
}

老板子类

public class BossWork extends AbstractWorkProcess{
    @Override
    public void jobContent() {
        System.out.println("喝茶");
    }
}

测试类

public class TestTemplate {
    public static void main(String[] args) {
//        AbstractWorkProcess workProcess = new ProgrammerWork();
        AbstractWorkProcess workProcess = new BossWork();
        workProcess.work();
    }
}

当我们实例化不同的子类的时候,具体的工作内容改变,但是总体的流程是不会变的。

打印结果:

打卡成功
喝茶
下班

举例二

还有一种场景,在Spring后处理器的应用中就有用到模板方法设计模式,在bean的生命周期中,可能会应用不同的后处理器,但是这些后处理器都不是固定的。我们来看示例代码。

public class MyBeanFactoryTest {

    public static void main(String[] args) {
        MyBeanFactory myBeanFactory = new MyBeanFactory();
        myBeanFactory.getBean();
    }

    static class MyBeanFactory{
        public void getBean(){
            Object bean = new Object();
            System.out.println("构造:"+bean);
            System.out.println("依赖注入:"+bean);
            System.out.println("初始化:"+bean);
        }
    }
}

MyBeanFactory的getBean方法,示例了获取bean的一个流程,分为构造,依赖注入,初始化,当我们需要在依赖注入这个阶段新增一个功能,比如解析@Autowired注解。如果不使用模板方法设计模式,我们可能会这样写:

public void getBean(){
    Object bean = new Object();
    System.out.println("构造:"+bean);
    System.out.println("依赖注入:"+bean);

    System.out.println("解析@Autowired");

    System.out.println("初始化:"+bean);
}

如果以后又有新增的功能比如要解析@Resource,我们再去改之前的代码吗,或者根本不需要去解析@Autowired注解呢?这样把代码写死了,对以后的扩展,以及使用的灵活性都造成了影响。

通过前面对模板方法模式的介绍,我们可以想到,可以把扩展功能增强的部分抽离出来,交给子类来实现,Spring采用的另外一种方式,我们来模拟一下:

public class MyBeanFactoryTest {

    public static void main(String[] args) {
        MyBeanFactory myBeanFactory = new MyBeanFactory();
        myBeanFactory.addBeanPostProcessor(new AutowiredBeanPostProcessor());
        myBeanFactory.getBean();
    }

    static class MyBeanFactory{

        private final List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

        public void getBean(){
            Object bean = new Object();
            System.out.println("构造:"+bean);
            System.out.println("依赖注入:"+bean);

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.inject();
            }

            System.out.println("初始化:"+bean);
        }

        public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor){
            beanPostProcessorList.add(beanPostProcessor);
        }
    }

    static interface BeanPostProcessor{
        void inject();
    }

    static class AutowiredBeanPostProcessor implements BeanPostProcessor{

        @Override
        public void inject() {
            System.out.println("解析@Autowired");
        }
    }
}

Spring的做法是将后处理器类抽象出来,称为一个接口,然后添加进后处理器集合中进行循环调用,这样就可以新增、减少、排序后处理器,比如要增加@Resource的处理,只需要再实现一个ResourceBeanPostProcessor,然后添加到后处理器集合中就行了。