模板方法模式(TemplateMethodPattern)

场景

AbstractRifle抽象步枪类,其中RifleTemplate为步枪模板方法,先装弹后射击。。

实现一

抽象模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class AbstractRifle {
/**
* 射击
*/
protected abstract void fire();

/**
* 装弹中
*/
protected abstract void reloading();

public final void RifleTemplate() {
reloading();
fire();
}
}

具体的模板类

1
2
3
4
5
6
7
8
9
10
11
public class M4A1 extends AbstractRifle {
@Override
protected void fire() {
System.out.println("M4A1射击,哒哒哒");
}

@Override
protected void reloading() {
System.out.println("M4A1装弹中,5.56mm");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class Ak47 extends AbstractRifle {
@Override
protected void fire() {
System.out.println("AK47射击,乌拉乌拉");
}

@Override
protected void reloading() {
System.out.println("AK47装弹中,7.62mm");
}
}

测试类

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
M4A1 m4A1 = new M4A1();
m4A1.RifleTemplate();
Ak47 ak47 = new Ak47();
ak47.RifleTemplate();
}
}

M4A1装弹中,5.56mm
M4A1射击,哒哒哒
AK47装弹中,7.62mm
AK47射击,乌拉乌拉

实现二

改变实验一中的需求,要求AK47不用填充子弹直接就可以射击,M4A1可以控制是否填充子弹。

抽象模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class AbstractRifle {
/**
* 射击
*/
protected abstract void fire();

/**
* 装弹中
*/
protected abstract void reloading();

public final void RifleTemplate() {
if (isReloading()) {
reloading();
}
fire();
}

/**
* 判断是否需要装弹
*
* @return true 需要 false 不需要
*/
protected boolean isReloading() {
return true;
}
}

具体的模板类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class M4A1 extends AbstractRifle {

public boolean reloading;

public M4A1(boolean reloading) {
this.reloading = reloading;
}

@Override
protected void fire() {
System.out.println("M4A1射击,哒哒哒");
}

@Override
protected void reloading() {
System.out.println("M4A1装弹中,5.56mm");
}

@Override
public boolean isReloading() {
return this.reloading;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Ak47 extends AbstractRifle {
@Override
protected void fire() {
System.out.println("AK47射击,乌拉乌拉");
}

@Override
protected void reloading() {
System.out.println("AK47装弹中,7.62mm");
}

@Override
protected boolean isReloading() {
return false;
}
}

测试类

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
M4A1 m4A1 = new M4A1(false); // 可以自己选择true或者false
m4A1.RifleTemplate();
Ak47 ak47 = new Ak47();
ak47.RifleTemplate();
}
}

M4A1射击,哒哒哒
AK47射击,乌拉乌拉

个人理解

我理解的模板模式就是和它的名字一样,定义了一个对象的一个框架,也就是模板,具体的实现交由子类来。还可以这样看,就是将几个对象的公有的一些方法抽象到一个类中,这些类要有一些共性,就比如本例子中的M4A1和AK47都是现安装子弹然后在射击,要是每个类都这些模板方法都不一样,那得弄出很多个模板方法。。。不过幸好还有实现二中的方式缓解这种情况。如果实在不可缓解我感觉是不是不合适用模板方法考虑别的设计模式。。或者对这个模板方法进行改进。这个模板方法模式重用性好,扩展性也好满足了开闭原则, 但是由于继承这层关系,万一那个抽象模板发生变动,其子类全部需要改变。。

总的来说,我感觉这个模板方法适合:假如有一个复杂的算法,其中某一个小部分交给子类去实现,而不能像本例子中的那样抽象模板中的模板方法全部都是由子类实现的。这可以参照AQS中在acquire方法中只有tryAcquire方法是交由子类实现的,调用本类的tryAcquire方法抛出一个异常,其它的算法均为自己实现。这样更加的灵活。

这个模板方法实际上不就是继承+重写吗。。AQS中的tryAcquire虽然叫模板方法模式,就是不同类继承重写这个方法。父类中某些方法具体在子类中延迟实现。

1
2
3
4
5
6
7
8
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}