模式名和分类
templateMethod
行为型模式
意图
定义一个操作中算法的骨架,而将一些步骤延迟到字类中。TemplateMethod使得字类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
动机
在Maven软件中,我们定义了3个生命周期,其中default生命周期里面有很多阶段,compile、package、install等等。Maven官方对每个阶段都写了实现方式,但是更灵活的软件必须支持其他第三方插件。来实现更多定制化的功能。
还记得maven项目中,我们对标签 的描述么,我们可以声明一个插件,并绑定特定的声明周期阶段。对maven本身的package或者install或者compile进行干涉。
就像这样:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
// 任务执行,插件可以执行多个任务
<executions>
<execution>
// 绑定了package阶段,很多插件不需要指定,因为插件的目标在编写时已经定义好默认绑定阶段
<phase>package</phase>
<goals>
// 通过goals配置指定要执行的插件目标
<goal>shade</goal>
</goals>
// 某些插件支持出传入参数,像mvn install -Dmaven.test.skip=true。里面的-D意味着
//后面是参数,真正的参数是maven.test.skip=true,这个也可以使用configuration配置,全局生效。
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>HelloWorld</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
适用性
在实际设计中,我们定义了操作步骤,但是允许不同实现。或者我们想让我们的设计支持各种不同的实现。
结构
参与者
- 父类,通常是抽象类 --AbstractClass
- 子类,实现具体方法的类 -- SubClass
协作
- 父类在模板方法中定义了操作的顺序,并设置方法final,意味着操作顺序不可变。
- 子类继承父类之后,重写真正的步骤执行方法
- 完美实现意图:父类定义算法的骨架,而子类定义其某些步骤的实现。
效果
前文提过,我们可以使得程序设计的扩展性更好,Maven的插件方式,spring的自定义扩展等等。
代码实例
例1:模式展示
https://www.runoob.com/design-pattern/template-pattern.html
// 步骤 1
// 创建一个抽象类,它的模板方法被设置为 final。
// Game.java
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
// 步骤 2
// 创建扩展了上述类的实体类。
// Cricket.java
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
// Football.java
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
// 步骤 3
// 使用 Game 的模板方法 play() 来演示游戏的定义方式。
// TemplatePatternDemo.java
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
例2:模拟maven的插件实现
只实现模板方法,实际操作更加复杂,但是过程大概如此。
在我们使用mvn build时,触发此方法。而其中的各个阶段方法,可以配置自定义插件,也可以使用maven的默认插件。
// maven 的default生命周期的build阶段。
public abstract class AbstractBuild {
public void build(){
initialize();
compile();
test();
packagee();
integrationTest();
deploy();
}
protected abstract void initialize();
protected abstract void compile();
protected abstract void test();
protected abstract void packagee();
protected abstract void integrationTest();
protected abstract void deploy();
}
例3 spring允许扩展自定义标签的解析
- 背景:spring具有很好的扩展性,允许我们扩展自定义标签,那么spring是如何实现的呢,这个方法是底层的解析过程。
- 此方法中,spring定义了解析过程,而具体的解析实现,定义在doParse(...)方法中,我们可以通过重写此方法,来解析我们自己定义的标签。
- 此过程是我在阅读spring源码时记下的笔记,特意记下模板模式的应用,再次处提出。具体解析过程不说了..只看他的模式。
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
// 在执行我们自定义的解析器中的方法之前,先执行一些准备工作,也可以叫做预解析,对beanClass、scope、lazyInit等属性的准备
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 这个是自定义解析器中重写的getBeanClass方法
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
// 如果没有重写getBeanClass方法,就看有没有重写getBeanClassName方法;
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
//如果存在父类,就使用父类的scope属性
// Inner bean definition must receive same scope as containing bean.
builder.setScope(containingBd.getScope());
}
if (parserContext.isDefaultLazyInit()) {
// 延迟懒加载
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
// 这个是我们自己写的解析器啦,还记得模版模式么!就是这个意思,在父类中定义要做的事,而将具体的实现延迟到子类;
// 我们可以自定义很多解析器,每个解析器都有自己的解析规则。只需要复写doParse方法就好了。典型的模版模式
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
已知应用
如上
- maven的插件实现
- spring的自定义标签解析