模板方法模式
Template Pattern:模板方法模式,也可以称为模板模式。属于GoF23种设计模式中行为型模式的一种。
给个书面定义:定义了一个操作中的算法骨架,而将一些步骤延迟到了子类之中实现。让子类呢可以在不改变大致的骨架之下,重新定义算法中的一些步骤。
简单的理解呢,其实和我们DIY配置了自己的电脑之后的组装一样,我们首先会选择一款合适的主板,这个主板呢就是一个大大的模板,里面有CPU啊,显卡啊,散热器啊,内存条啊等等的接口。这块主板呢就对应着我们组装操作中的骨架,而CPU,显卡,内存条是组装中的各种各样的的步骤。一旦主板确定好了,在想换的话只能换主板了,这样整个骨架就换了,但是呢对于CPU,显卡什么的我们却是可以自己定制的,这就相当于是里面的一些特定的部分了。
又或者说,可以理解为,都需要接收的九年义务教育,稳稳当当的起码可以享受九年的教育生涯是吧,这也是一个整体的框架,不能说想改啊什么的吧,来个十二年义务教育?这是不可能想想就实现的,但是里面具体是个什么教育法呢?那就不确定了,笔者记得有人教版的教材, 鄂教版 的教材,北师大版的教材,好像还有个苏教版的教材。这个里面呢“九年义务教育”就是一个大大的骨架,而各种不同的教育版本的教材就是重新被定义实施的一些步骤了。
结构
对照着类图简单的分析一下,这个模式其实也挺简单的,总共就两个对象:
- 抽象模板对象:这个呢,就是一个建立骨架的过程,将整个模板/骨架搭建好,对于一些比较复杂的模板如果有着一些不需要改变的部分,这里也就直接可以写了,子类中就少了大部分的重复的代码了。
- 具体对象:继承抽象模板对象。这就是实际情况下需要使用的对象了,需要对于骨架中的一些特殊的位置进行符合业务需求的定制化生成为需求服务的实际对象。
举例说明
- 用的最多的应该就是各大高大上的框架的集成的使用了。对于火热的Spring关于数据库的集成就有模板方法模式的体现。比如说Spring Data JPA的使用,直接定义一个实体类,一个继承指定接口的接口,我们就可以实现简单的增删改查操作了,神奇吧,有木有感觉到什么都没做的感觉?你没有听错,提供一个实体类,一个继承指定接口的接口,你就可以进行基础的CRUD操作了,所有的东西,都被封装成了一个模板。当你想要定制化其他的CRUD操作的时候,也可以,你可以通过JPQL,或者一定格式的查询方法实现,但是你不需要建立数据库的连接,也不需要释放数据库的连接,因为都已被模板定义好了。
- 好理解一点的例子可以看一下上面笔者对于九年义务教育和装机的比方,应该是比较容易理解的。
- 又或者可以说,现在各大小区啊,花园啊,公寓啊,一眼望去所有的楼的外观都是一样的有木有?这也是一个大大的模板骨架啊。但是,但是,重点是里面有单人间,双人间,三人间,四人间,等等,这从外观看不出来吧,但是确实是存在的啊,这些房间呢就是定制化的一些内容啦~!
注意
- 模板方法模式有一个很重要的应用,笔者自己的感觉啊,就是在重构的时候。重构是一个很重要的活,也是一个很需要去做的事情。在重构中,模板方法模式可以给我们从各个子类中抽取去重复的代码到父类里面去,减少重复冗余的代码。
- 模板方法尤其适合那种很繁琐但是又很无聊,并且会大量重复的代码编写的时候,比如使用JDBC,需要七步走时不时,数据库连接是要的吧,数据库操作对象是要的吧,用完了我还得释放吧,本来只是一条查询语句,明明一句话的事情,硬是写了十几二十行,忽然,又加一个查询操作,再来这么十几二十行,等到下次复查的时候,都找不到自己孤零零的唯一有用的那行代码了好吧…这里将这些重复的代码抽取出来做成一个模板,那简直不要太舒服。
- 哦,对,还有一个要注意的就是,对于模板方法,一般是直接上final关键词,不允许子类重写的,也算是一种防护的措施吧。
一个小DEMO
-
场景
有个脑筋急转弯说“把大象装进冰箱要几步???”,嘿嘿,都知道,三步,打开冰箱,让大象进去,关上冰箱,嘿,完了,是吧!但是呢,不仅仅是大象?我要是放老虎呢?狮子呢?猴子呢?都是三步,只是操作的对象不一样而已了。
-
首先能将骨架搭起来 注意final关键字哟
package com.template; /** * 模板方法模式——抽象模板 * 这是搭建整体架构的地方 * @author WQ */ public abstract class Framework { /** * 第一步都一样,开门就行 */ public void step1() { System.out.println("第一步:打开冰箱门~"); } /** * 第二步是需要定制化的,因为对象不同撒 */ public abstract void step2(); /** * 最后一步,就需要把门关上了 */ public void step3() { System.out.println("第三步:关上冰箱门~"); } /** * 模板方法模式,肯定是有模板方法了, * 控制这三步的流程不能出错啊,不能先关门吧,再说冰箱本来就是关的呀 */ public final void frame() { //按照顺序执行 step1(); step2(); step3(); } }
-
装个大象+老虎
package com.template; /** * 模板方法模式——具体对象 * 嗯,来个大象 * @author WQ */ public class Elephant extends Framework{ /** * 定制化 */ @Override public void step2() { System.out.println("第二步:将大象装进去!!!"); } } //---------------------------------------------------------------- package com.template; /** * 模板方法模式——具体对象 * 嗯,再来个老虎 * @author WQ */ public class Tiger extends Framework{ /** * 定制化 */ @Override public void step2() { System.out.println("第二步:将老虎装起去!!!"); } }
-
编个测试类
package com.template; /** * 模板方法模式——测试类 * @author WQ */ public class Main { public static void main(String[] args) { System.out.println("问:把大象装进冰箱要几步?"); System.out.println("答:"); new Elephant().frame(); System.out.println("再问:把老虎装进冰箱要几步?"); System.out.println("再答:"); new Tiger().frame(); } }
-
测试走一泼
问:把大象装进冰箱要几步? 答: 第一步:打开冰箱门~ 第二步:将大象装进去!!! 第三步:关上冰箱门~ 再问:把老虎装进冰箱要几步? 再答: 第一步:打开冰箱门~ 第二步:将老虎装起去!!! 第三步:关上冰箱门~
完成!!!