模式介绍模式的结构与实现
钩子方法 模板方法模式的注意事项和细节
模式介绍
模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变算法结构的情况下重新定义该算法的某些特定步骤。是一种行为型设计模式。
该模式的主要优点如下:
1.它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。2.它在父类中提取了公共的部分代码,便于代码复用。3.部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
该模式主要缺点如下:
1.对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现地复杂度。2.父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读难度。3.由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
模式的结构与实现
需求案例:编写制作豆浆的程序,说明如下
制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎通过添加不同的配料,可以制作出不同口味的豆浆选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的(红豆、花生豆浆等等)
类图:
模式实现如下:
定义抽象类
public abstract class SoyaMilk {
// 模板方法,make,final修饰,防止子类覆盖
final void make() {
select();
addCondiments();
soak();
beat();
}
// 1. 选材料
void select() {
System.out.println("第一步:选择好的新鲜的黄豆");
}
// 2. 添加不同的配料,抽象方法,由子类具体实现
abstract void addCondiments();
// 3. 浸泡
void soak() {
System.out.println("第三步,黄豆和配料开始浸泡");
}
// 4. 打碎
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎");
}
}
子类继承
public class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println("加入上好的红豆");
}
}
public class PeanutSoyaMilk extends SoyaMilk{
@Override
void addCondiments() {
System.out.println("加入上好的花生");
}
}
客户端调用
public class Client {
public static void main(String[] args) {
// 制作红豆豆浆
System.out.println("----制作红豆豆浆----");
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
redBeanSoyaMilk.make();
// 制作花生豆浆
System.out.println("----制作红豆豆浆----");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
}
}
运行结果:
----制作红豆豆浆---- 第一步:选择好的新鲜的黄豆 加入上好的红豆 第三步,黄豆和配料开始浸泡 第四步:黄豆和配料放到豆浆机去打碎 ----制作红豆豆浆---- 第一步:选择好的新鲜的黄豆 加入上好的花生 第三步,黄豆和配料开始浸泡 第四步:黄豆和配料放到豆浆机去打碎 Process finished with exit code 0钩子方法
在模板方法模式的父类中,我们可以定义一个方法,它不用做任何事情,子类可以视情况要不要覆盖,该方法成为“钩子”。
代码演示:还是用上面的案例,比如制作纯豆浆,不添加任何配料,请使用钩子方法对前面的模板方法进行改造。
抽象类修改
public abstract class SoyaMilk {
// 模板方法,make,final修饰,防止子类覆盖
final void make() {
select();
if (customerWantCondiments()) {
addCondiments();
}
soak();
beat();
}
// 1. 选材料
void select() {
System.out.println("第一步:选择好的新鲜的黄豆");
}
// 2. 添加不同的配料,抽象方法,由子类具体实现
abstract void addCondiments();
// 3. 浸泡
void soak() {
System.out.println("第三步,黄豆和配料开始浸泡");
}
// 4. 打碎
void beat() {
System.out.println("第四步:黄豆和配料放到豆浆机去打碎");
}
// 钩子方法,决定是否需要添加配料
boolean customerWantCondiments() {
return true;
}
}
子类继承
public class PeanutSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println("加入上好的花生");
}
}
public class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println("加入上好的红豆");
}
}
纯豆浆,就不用加入配料,如下
public class PureSoyaMilk extends SoyaMilk{
@Override
void addCondiments() {
// 空实现
}
@Override
boolean customerWantCondiments() {
return false;
}
}
客户端访问
public class Client {
public static void main(String[] args) {
// 制作红豆豆浆
System.out.println("----制作红豆豆浆----");
SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk();
redBeanSoyaMilk.make();
// 制作花生豆浆
System.out.println("----制作红豆豆浆----");
SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
peanutSoyaMilk.make();
// 制作纯豆浆
System.out.println("----制作纯豆浆----");
SoyaMilk pureSoyaMilk = new PureSoyaMilk();
pureSoyaMilk.make();
}
}
运行结果如下:
----制作红豆豆浆---- 第一步:选择好的新鲜的黄豆 加入上好的红豆 第三步,黄豆和配料开始浸泡 第四步:黄豆和配料放到豆浆机去打碎 ----制作红豆豆浆---- 第一步:选择好的新鲜的黄豆 加入上好的花生 第三步,黄豆和配料开始浸泡 第四步:黄豆和配料放到豆浆机去打碎 ----制作纯豆浆---- 第一步:选择好的新鲜的黄豆 第三步,黄豆和配料开始浸泡 第四步:黄豆和配料放到豆浆机去打碎 Process finished with exit code 0
模板方法模式的注意事项和细节
基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类模板方法或者已经实现的某些步骤,子类就会继承这些修改。实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。该模式的不足之处:每一个不同的实现都需要一个子类,导致类个数增加,系统庞大。一般模板方法都加上final,防止子类重写。



