什么是模板模式
模板模式(Template Pattern),有的地方也称为模板方法模式(Template Method Pattern),是一种在父类中定义程序执行规则,在子类中实现具体处理逻辑的设计模式。
在模板模式中,父类负责制定一套通用的程序执行流程,子类严格遵守这套流程,流程中包含多个抽象步骤,而每个步骤的具体实现则依赖于每个具体的子类。
UML类图
模板模式UML类图如下:
其中,父类AbsTemplate
定义了模板方法templateMethod()
,模板方法用来制定程序的执行流程,这套流程是固定的,不可被子类重写,在templateMethod()
的实现中依次调用了抽象方法method1()
、method2()
、method3()
,即完整的程序执行流程包含了method1()
、method2()
、method3()
3个步骤,而method1()
、method2()
、method3()
是抽象的,并未具体实现,具体实现交给了子类ConcreteImplA
和ConcreteImplB
。
示例
知道了模板模式的概念和框架结构,我们对模板模式已经有了一个初步认识,接下来,我们以动物为例来演示模板模式,以进一步加深我们的理解。
动物是一个泛指的概念,其下包含许多具体种类,如鸟类、鱼类、兽类等,不同种类的动物在外观、习性方面存在差异,但也存在很多共性,例如动物都要吃东西、活动和休息,只是不同种类动物的食物偏好、活动形式和休息方式可能不一样,我们刚好可以以此来演示模板模式。
首先,我们抽象出动物父类AbsAnimal
,并把动物的生活(live)定义为一个模板,在这个模板里包含了生活的一些基本环节,例如吃东西(eat)、活动(move)和睡觉(sleep),因此,我们可以定义一个不可变的live()
方法,同时抽象出eat()
、move()
、sleep()
3个抽象方法,然后在live()
中依次调用eat()
、move()
、sleep()
,这样,一个关于动物生活的模板就定义好了。AbsAnimal
代码如下:
package com.xy.designpatterns.template.sample
/**
* 动物抽象类
*/
abstract class AbsAnimal {
/**
* 模板方法
* 表示动物每天的生活
*/
fun live() {
eat()
move()
sleep()
}
/**
* 吃东西
*/
abstract fun eat()
/**
* 活动
*/
abstract fun move()
/**
* 睡觉
*/
abstract fun sleep()
}
AbsAnimal
仅表示抽象的动物,抽象的动物是不具体的,只知道要做哪些事情,而不明确每件事情该怎么去做,所以我们还需要解决表达“如何做”的问题,怎么解决呢?我们需要定义具体的动物类,即继承AbsAnimal
的子类,子类通过重写AbsAnimal
中的抽象方法,来说明每种动物具体是怎么吃东西的、怎么活动的、怎么睡觉的。在这里,我们定义3个具体的动物类:Bird
、Fish
、Lion
,继承自AbsAnimal
,并实现AbsAnimal
中的抽象方法,代码如下:
Bird
:
package com.xy.designpatterns.template.sample
/**
* 小鸟
*/
class Bird : AbsAnimal() {
override fun eat() {
println("小鸟吃昆虫")
}
override fun move() {
println("小鸟在天空飞翔")
}
override fun sleep() {
println("小鸟在树上的鸟窝里面睡觉")
}
}
Fish
:
package com.xy.designpatterns.template.sample
/**
* 小鱼
*/
class Fish : AbsAnimal() {
override fun eat() {
println("小鱼吃水草")
}
override fun move() {
println("小鱼在水里游动")
}
override fun sleep() {
println("小鱼钻进沙里睡觉")
}
}
Lion
:
package com.xy.designpatterns.template.sample
/**
* 狮子
*/
class Lion : AbsAnimal() {
override fun eat() {
println("狮子吃羚羊")
}
override fun move() {
println("狮子在草原奔跑")
}
override fun sleep() {
println("狮子趴在树下睡觉")
}
}
这样,Bird
、Fish
、Lion
3个具体动物类,便在AbsAnimal
定义好的大的模板框架内,实现了对吃东西、活动、睡觉的自定义。
接下来,我们通过测试代码来看看示例代码的运行效果。
模板模式场景模拟器TemplatePatternSceneSimulator
代码如下:
package com.xy.designpatterns.template
import com.xy.designpatterns.ISceneSimulator
import com.xy.designpatterns.template.sample.AbsAnimal
import com.xy.designpatterns.template.sample.Bird
import com.xy.designpatterns.template.sample.Fish
import com.xy.designpatterns.template.sample.Lion
/**
* 模板模式场景模拟器
*/
class TemplatePatternSceneSimulator : ISceneSimulator {
override fun run() {
println("============模板模式示例============")
println("Bird的生活:")
val bird: AbsAnimal = Bird()
bird.live()
println("====================================")
println("Fish的生活:")
val fish: AbsAnimal = Fish()
fish.live()
println("====================================")
println("Lion的生活:")
val lion: AbsAnimal = Lion()
lion.live()
println("============模板模式示例============")
}
companion object {
/**
* 运行场景
*/
fun run() {
TemplatePatternSceneSimulator().run()
}
}
}
测试入口类Main
代码如下:
package com.xy.designpatterns.template
import org.junit.jupiter.api.Test
/**
* 模板模式测试类
*/
class Main {
/**
* 演示模板模式
*/
@Test
fun main() {
TemplatePatternSceneSimulator.run()
}
}
我们执行Main#main()
方法后,控制台输出如下日志:
============模板模式示例============
Bird的生活:
小鸟吃昆虫
小鸟在天空飞翔
小鸟在树上的鸟窝里面睡觉
====================================
Fish的生活:
小鱼吃水草
小鱼在水里游动
小鱼钻进沙里睡觉
====================================
Lion的生活:
狮子吃羚羊
狮子在草原奔跑
狮子趴在树下睡觉
============模板模式示例============
可以看到,Bird
、Fish
、Lion
都遵循了AbsAnimal
制定的模板框架(live),并各自实现了对框架内每个抽象环节(eat、move、sleep)的自定义。
总结
简单来说,模板模式就是抽取相似性事物的共性,并将这些共性进行封装,然后对差异性进行隔离,即把差异性通过抽象的方式暴露出来,交还给具体的事物去实现。
在平时的开发工作中,如果我们遇到的问题具有多种不同的场景,不同场景之间既有差异又有共性,那么就可以考虑使用模板模式来进行设计。