模版方法模式

定义

        在父类中定义一个操作的通用具体步骤,将这些步骤的实现细节交给子类来完成。这样可以使得子类在不更改操作的结构下,可以重新定义该操作的具体步骤。


简单实例

        定义都是空洞乏味的,让我们以一个简单的例子来说明该模式是如何运作的。

        作为一个上班族,每天从起床睁开眼睛到坐在办公室工作,这一期间的动作执行流程基本固话了,都是穿衣服--刷牙洗脸--吃早饭--开车(假装有车)--工作,那么怎么用代码来描述这种场景呢,很简单。


代码示例

        首先定义一个抽象父类

public abstract class AbstractHuman {

    //穿衣服
    public abstract void wear();

    //刷牙洗脸
    public abstract void wash();

    //吃早饭
    public abstract void haveBreakfast();
    
    //开车
    public abstract void drive();

    //工作
    public abstract void work();

    //起床到工作这一期间动作的执行流程
    public abstract void oper();
    
}

        描述我的动作代码

public class Yang extends AbstractHuman {

    @Override
    public void wear() {
        System.out.println("穿衣服");
    }

    @Override
    public void wash() {
        System.out.println("洗漱");
    }

    @Override
    public void haveBreakfast() {
        System.out.println("吃早饭");
    }

    @Override
    public void drive() {
        System.out.println("开车");
    }

    @Override
    public void work() {
        System.out.println("上班");
    }

    @Override
    public void oper() {
        this.wear();
        this.wash();
        this.haveBreakfast();
        this.drive();
        this.work();
    }

}

        当然上班族不止我一个,隔壁的老王说他的动作和我一样,这代码也给他来一份。那好,我仅仅改了个类名,便完成了如此艰巨的任务。仔细想一想,代码里的oper出现了重复,甚至可以说完全相同,我们大可以将oper的实现放在抽象类中,毕竟抽象是对所有子类的共性封装。


代码改进

        将oper的实现放入抽象父类中

public abstract class AbstractHuman {

    //穿衣服
    public abstract void wear();

    //刷牙洗脸
    public abstract void wash();

    //吃早饭
    public abstract void haveBreakfast();

    //开车
    public abstract void drive();

    //工作
    public abstract void work();

    //起床到工作这一期间动作的执行流程
    //父类规定好的步骤顺序,不希望子类去重写
    public final void oper() {
        this.wear();
        this.wash();
        this.haveBreakfast();
        this.drive();
        this.work();
    }

}

        修改子类Yang

public class Yang extends AbstractHuman {

    @Override
    public void wear() {
        System.out.println("穿衣服");
    }

    @Override
    public void wash() {
        System.out.println("洗漱");
    }

    @Override
    public void haveBreakfast() {
        System.out.println("吃早饭");
    }

    @Override
    public void drive() {
        System.out.println("开车");
    }

    @Override
    public void work() {
        System.out.println("上班");
    }

}

        在Main方法内调用new Yang().oper()方法,即可输出Yang的动作流程。

        以上就是模版方法,很简单。

        但是!!!!隔壁的小花找到我,说这代码挺不错,能不能给她来一份,不过她早上得化妆,并且由老王送她上班,她不需要开车(想到这里,我不禁留下单身狗的泪水)。我看着现在的代码陷入了沉思,有了!可以在抽象父类中添加化妆抽象方法,并且使用钩子方法来决定是否需要化妆与开车的步骤。


代码改进

        所谓钩子方法,则是由子类的方法的返回值决定公共部分的执行结果,现在将抽象父类改造。

public abstract class AbstractHuman {

    //穿衣服
    public abstract void wear();

    //刷牙洗脸
    public abstract void wash();

    //吃早饭
    public abstract void haveBreakfast();

    //化妆
    public abstract void makeUp();

    //开车
    public abstract void drive();

    //工作
    public abstract void work();

    //是否化妆
    public abstract boolean isMakeup();

    //是否开车
    public abstract boolean isDrive();

    //起床到工作这一期间动作的执行流程
    public void oper() {
        this.wear();
        this.wash();
        this.haveBreakfast();
        if (isMakeup()) {
            this.makeUp();
        }
        if (isDrive()) {
            this.drive();
        }
        this.work();
    }

}

        描述小花动作的类

public class Hua extends AbstractHuman {

    @Override
    public void wear() {
        System.out.println("穿衣服");
    }

    @Override
    public void wash() {
        System.out.println("洗漱");
    }

    @Override
    public void haveBreakfast() {
        System.out.println("吃早饭");
    }

    @Override
    public void makeUp() {
        System.out.println("化妆");
    }

    @Override
    public void drive() {
        System.out.println("开车");
    }

    @Override
    public void work() {
        System.out.println("上班");
    }

    @Override
    public boolean isMakeup() {
        return true;
    }

    @Override
    public boolean isDrive() {
        return false;
    }

}

        Main方法调用new Hua().oper(),输出如下:

        有了钩子方法,模版方法模式更加完美了。


总结

        模板方法模式就是在抽象父类中的模板方法中按照一定的规则和顺序调用基本方法,而这些基本方法在具体的子类中实现。把认为是不变部分的步骤封装到父类实现,而可变部分的则可以通过继承来继续扩展。这种方式非常容易扩展。只要增加一个子类,实现父类的基本方法就可以了。例如在一个框架中,你需要扩展框架的某个功能,相关文档中总是有这样的一句话——你需要继承某个抽象类,覆盖抽象类中的相关方法,之后调用execute()方法即可。

        模板方法模式适用在重要、复杂的算法上,可以把核心算法设计为模板方法,再通过钩子方法约束模版方法,之后模版方法内的相关细节功能则由各个子类实现。