[设计模式] 模板方法模式 Template method 行为型 设计模式(二十六)

计算机科学 计算机科学 1705 人阅读 | 0 人回复

模板方法模式 Template method

image.png

上图为网上百度的一份简历模板截图

image.png

相信大家都有求职的经历,那么必然需要简历,写简历的时候,很可能你会网上检索一份简历模板,使用此模板的格式,然后替换为你的内容。

我们从小就有语文课,逢考必有作文,而学习的途径之一就是参考优秀的范文,学习了解他们的结构,风格等。

image.png

以上就是现实世界中的模板,模板的概念随处可见,所有的工业制品哪个不是模具生产的?

在程序设计中,也有模板的概念

在软件开发过程中,可能经常会用到类似的处理逻辑,但是可能又有一些细节的差异

比如做菜,不管做什么菜,基本上都离不开买菜,洗菜,切菜,做菜几个主要步骤

这几个步骤中,只有做菜的差别最大,不同的菜不同的做法,而买菜洗菜切菜的过程,却基本类似

我们常常希望能够定义一个通用的处理框架,然后将一部分实现细节交由子类进行处理

也就是面向框架进行编程,而不是每次都复制粘贴修改代码,具体的细节依靠子类确定,这就是模板的初衷。

比如定义一个做菜的抽象类,实现了买菜,洗菜,切菜,做菜方法定义为抽象方法留待子类实现

那么,回锅肉和大头菜就可以通过继承扩展做菜类,只需要实现做菜的环节即可。

意图

定义一个操作中的算法的骨架,而将一些步骤延时到子类中。

TemplateMethod使得子类可以不改变一个算法的结构即可重新定义算法的某些特定步骤。

结构

image.png

抽象模板角色AbstractClass

定义一个或者多个抽象步骤,这些抽象操作叫做基本操作

他们可能是一个复杂操作的组成步骤

实现类角色ConcreteClass

实现父类所定义的一个或者多个抽象方法

每一个抽象模板都可以有任意多个具体的模板角色与之对应,而实际中,一般不止一个

代码示例

定义做菜步骤:买菜,洗菜,切菜,做菜。

买菜、洗菜、切菜都一样,做菜不同菜不同做法

上菜将他们步骤进行打包

package template;

public abstract class 做菜 {
    public void 买菜(){
        System.out.println("买菜...");
    }
    public void 洗菜(){
        System.out.println("洗菜...");
    }
    public void 切菜(){
        System.out.println("切菜...");
    }
    public abstract void 做菜();

    public void 上菜(){
        买菜();
        洗菜();
        切菜();
        做菜();
        System.out.println("客官,菜来了~~~");
    }
}

做回锅肉和做大头菜都重写了“做菜”的方法

package template;

public class 做回锅肉 extends 做菜 {
@Override
public void 做菜() {
      System.out.println("做回锅肉...");
    }
}
package template;
public class 做大头菜 extends 做菜 {
@Override
public void 做菜() {
  System.out.println("做大头菜...");
 }
}

测试代码

image.png

方法分类

通常模板模式中会涉及到两类方法,模板方法和基本方法

模板方法指的是定义在抽象类中,把基本方法组合在一起形成复杂逻辑的方法,通常子类是不修改这个方法的

模板方法给出来顶层的逻辑框架。

比如上面的“上菜”,上菜方法调用了“买菜,洗菜,切菜,做菜”

可以有任意多个的模板方法

另一类就是基本方法了,基本方法就是复杂方法的组成部分

基本方法又有几种形式

抽象方法,具体方法,钩子方法

抽象方法:abstract定义,子类实现

具体方法:抽象类具体实现

钩子方法:抽象类提供默认实现的方法,经常是一个空实现,好处是子类不是必须实现

前面说到,模板方法定义了顶层的框架逻辑,而且子类一般不修改,直接继承。

可以通过钩子方法对顶层框架逻辑进行微调

比如上面做菜的示例中,上菜的环节中,新增加一个方法用来判断是否需要切菜

image.png

做大头菜类中,重写这个方法,做大头菜,不切了

image.png

再来看看打印结果,大头菜,没切就做了。。。。。

image.png

总结

模板方法模式的根本在于共性的提取与解题步骤框架化

通常使用继承机制完成这一目标

继承使得类型的等级结构易于理解,层次分明,非常适合抽象化的设计

但是继承随之而来的强耦合,也将会导致很多的不便,比如打破了封装,父类向子类暴露

不能在运行时动态更改,父类改变,子类很可能也需要改变

所以继承是一把双刃剑,使用不当也会导致很大的问题。

但是,不能因噎废食,个人认为对继承的态度应该是不滥用,不弃用

模板模式也可以用于方法层次上方法的拆解,如果一个方法中有很多的代码逻辑步骤

那么,可以借助于模板模式定义解题步骤,将步骤进行拆解

比如原方法为

step()

拆分后为

step(){

step1();

step2();

....

}

原来的step的方法就相当于抽象类,step1,step2就相当于具体的子类

模板模式的根本就是共性的提取以及解题步骤框架化(就是步骤分明处理)

所以千万不要认为模板就仅仅只是继承,实现接口就表示模板的概念了么?个人认为仍旧是

没有模板方法,仅仅是基本方法就不是模板模式了么?个人认为仍旧是

模板方法仍旧是依赖倒置原则的实现方案。

只不过模板模式相对于面向抽象编程,又进一步期望父类提供更多给子类,比如算法逻辑框架

将部分职责延迟到子类。

借助于钩子方法的形式,可以引入更多的灵活性,子类可以对父类的整体逻辑做出微调

达到了反向控制的效果---子类控制了父类方法的细节步骤

涉及到共性提取或者框架步骤分割的都可以考虑模板模式

common_log.png 转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-125-1-1.html

关注下面的标签,发现更多相似文章

文章被以下专栏收录:

    黄小斜学Java

    疯狂的字节X

  • 目前专注于分享Java领域干货,公众号同步更新。原创以及收集整理,把最好的留下。
    包括但不限于JVM、计算机科学、算法、数据库、分布式、Spring全家桶、微服务、高并发、Docker容器、ELK、大数据等相关知识,一起进步,一起成长。
热门推荐
[若依]微服务springcloud版新建增添加一个
[md]若依框架是一个比较出名的后台管理系统,有多个不同版本。
[CXX1300] CMake '3.18.1' was not
[md][CXX1300] CMake '3.18.1' was not found in SDK, PATH, or
海康摄像头接入 wvp-GB28181-pro平台测试验
[md]### 简介 开箱即用的28181协议视频平台 `https://github.c