设计模式——模板方法模式

158 阅读6分钟

一、概述

模板方法模式也是一种行为型模式,定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法模式主要用到了java的继承机制,一个抽象类公开定义了执行它的方法的方式/模板,它的子类可以按需要重写方法实现,但调用将在抽象类中定义的方式进行。

什么是模版?就举个我们写的简历,通常我们都会有自我介绍、工作经历、项目经历、技能熟练程度等等这些固定的内容块,这些固定块是不可或缺的,即不能更改,只是每个人会根据自己不同的工作经验,每块内容都会是不同的,这就是模版。虽然不能在这个简历模版中修改固定的内容块,但有的人为了让自己的简历更丰富,会在简历中增加在校获得的奖项、个人作品等等;但有的人就不喜欢写这些东西,所有模版方法模式可以在子类中扩展自己的其他方法。

二、使用

就拿上面书写个人简历模版来做说明。前面说到,个人简历模版有4个固定的公共方法,并且是有先后顺序的,所以我们就需要把它封装在一个抽象的类中(这里不一定时抽象类,普通的类也可以;方法也不一定是抽象方法,普通方法也可以)。

public abstract class AndroidResumeTemplate {

    //自我介绍
    protected abstract void selfIntroduction();
    
    //工作经历
    protected abstract void workExperience();
    
    //项目经历
    protected abstract void projectExperience();
    
    //技能熟练度——提供默认实现
    protected void skillProficiency(){
        //Android程序员必须能够熟练使用Java语言
        Log.e("XXX","技能熟练度:熟练Java");
    }

    //写简历
    protected void write(){
        selfIntroduction();
        workExperience();
        projectExperience();
        skillProficiency();
        other();
    }

    //其他
    protected void other(){

    }
}

可以看到通过在父类中调用write方法,调用了各个模版方法,以及控制先后顺序;其中other方法用来填写简历的附加内容,如果有的话,可以重写这个方法。接下来创建3个不同人物的具体简历。

/**
 * 张三个人简历
 */
public class ZhangSanResume extends AndroidResumeTemplate {

    private static final String TAG = "XXX";
    private String name;

    public ZhangSanResume(String name){
        this.name = name;
    }

    @Override
    protected void selfIntroduction() {
        Log.e(TAG, name + ",个人介绍:" + "本人27岁,毕业于哈佛大学,现有4年工作经验,希望薪资是25k");
    }

    @Override
    protected void workExperience() {
        Log.e(TAG, name + ",工作经验:" + "2014-2016在谷歌工作2年;2017-2020在微软工作3年");
    }

    @Override
    protected void projectExperience() {
        Log.e(TAG, name + ",项目经验:" + "开发了Facebook;开发了淘宝App;开发了OPPO应用商店...");
    }
}
/**
 * 李四个人简历
 */
public class LiSiResume extends AndroidResumeTemplate {

    private static final String TAG = "XXX";
    private String name;

    public LiSiResume(String name){
        this.name = name;
    }

    @Override
    protected void selfIntroduction() {
        Log.e(TAG, name + ",个人介绍:" + "本人29岁,毕业于成都电子科技大学,现有6年工作经验,希望薪资是30k");
    }

    @Override
    protected void workExperience() {
        Log.e(TAG, name + ",工作经验:" + "2013-2015在阿里巴巴工作2年;2016-2018在腾讯工作2年;2019-至今在网易工作2年");
    }

    @Override
    protected void projectExperience() {
        Log.e(TAG, name + ",项目经验:" + "开发了手机支付宝;开发了腾讯QQ;开发了企业微信App...");
    }

    @Override
    protected void skillProficiency() {
        //不使用默认实现,因为我的技能更加厉害,所以我要重写这个方法
        Log.e(TAG, name + ",技能熟练度:" + "精通C/C++;精通Java;精通Kotlin;精通SQL数据库");
    }

    @Override
    protected void other() {
        schoolReward();
    }

    //在校获奖
    private void schoolReward(){
        Log.e(TAG, name + ",在校获得奖项:" + "获得全国大学生数学建模一等奖;获得成都电子科技大学编程大赛金牌");
    }
}
/**
 * 王五个人简历
 */
public class WangWuResume extends AndroidResumeTemplate {

    private static final String TAG = "XXX";
    private String name;

    public WangWuResume(String name){
        this.name = name;
    }

    @Override
    protected void selfIntroduction() {
        Log.e(TAG, name + ",个人介绍:" + "本人33岁,毕业于西安电子科技大学,现有9年工作经验,希望薪资是50k");
    }

    @Override
    protected void workExperience() {
        Log.e(TAG, name + ",工作经验:" + "2011-2014在华为工作3年;2014-2017在小米工作3年;2017-2020在谷歌工作4年");
    }

    @Override
    protected void projectExperience() {
        Log.e(TAG, name + ",项目经验:" + "开发了华为手机应用商店;开发了小米MIUI操作系统;参与开发Android 11系统...");
    }

    @Override
    protected void skillProficiency() {
        //不使用默认实现,因为我的技能更加厉害,所以我要重写这个方法
        Log.e(TAG, name + ",技能熟练度:" + "精通C/C++;精通Java;精通Linux系统;精通各种音视频开发....");
    }

    @Override
    protected void other() {
        schoolReward();
        personalAchievement();
    }

    //在校获奖
    private void schoolReward(){
        Log.e(TAG, name + ",在校获得奖项:" + "获得全国大学生编程设计ACM竞赛一等奖;获得全国AI人工智能竞赛二等奖");
    }

    //个人作品成果
    private void personalAchievement(){
        Log.e(TAG, name + ",个人作品:" + "设计出在线火车票订票系统;团体研发出餐饮点点餐系统App");
    }
}

从上面的3个具体的简历中,我们看出,张三没有重写skillProficiency方法,使用了原有的默认实现;而李四自己觉得个人技能更加的厉害,重写此方法,并添加了在线获奖情况;同样,王五也觉得自己是个大神,重写了skillProficiency方法,并添加了个人作品,是的自己的简历更加丰富。

最后进行测试,得到以下输出。我们可以看到

AndroidResumeTemplate zhang = new ZhangSanResume("张三");
zhang.write();
Log.e(TAG, "--------------------");
AndroidResumeTemplate li = new LiSiResume("李四");
li.write();
Log.e(TAG, "--------------------");
AndroidResumeTemplate wang = new WangWuResume("王五");
wang.write();

三、总结

模版方法模式使用起来是比较简单的,虽然简单,但也是有很多的优点:

  • 在抽象类中封装了固定不会改变的部分,留给子类可以进行扩展的方法;
  • 把公共的方法抽离出来,便于我们维护;
  • 子类不需要关心具体的全局流程,只需要重写父类的方法来实现自己的逻辑;
  • 所有的行为完全交给父类来控制,通过模版方法来对子类进行约束,这样能够简化子类的复杂度。

模版方法模式也会带来一些缺点:

  • 模版方法模式使用继承,当父类需要改变一些抽象方法时,所有的子类都会受到影响;
  • 规定了模版方法的步骤流程,子类就需要根据这个流程走下去,这样就增加了代码的阅读难度,会变得不好理解。

github地址:github.com/leewell5717…

四、参考

Java设计模式百例 - 模板方法模式