模板方法模式
行为型模式
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
介绍
意图: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决: 一些方法通用,却在每一个子类都重新写了这一方法。
何时使用: 有一些通用的方法。
如何解决: 将这些通用算法抽象出来。
关键代码: 在抽象类实现,其他步骤在子类实现。
具体实现
制作豆浆的流程 选材-->添加配料-->浸泡-->放到豆浆机打碎。
选材、浸泡和放到豆浆机打碎对于制作每种口味的豆浆都是一样。
第一步:创建豆浆模板类
public abstract class SoyaMilk {
//模板方法 , make , 模板方法可以写成final,不让子类去覆盖.
final void make() {
select();
addCondiments();
soak();
beat();
}
//选材料
void select() {
System.out.println("选择新鲜的黄豆...");
}
//添加不同的配料
abstract void addCondiments();
//浸泡
void soak() {
System.out.println("黄豆和配料开始浸泡...");
}
//浸泡
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();
}
}
运行如下:
---------制作红豆豆浆------
选择新鲜的黄豆...
加入最好的红豆
黄豆和配料开始浸泡...
黄豆和配料放到豆浆机开始打碎...
---------制作花生豆浆------
选择新鲜的黄豆...
加入最好的花生
黄豆和配料开始浸泡...
黄豆和配料放到豆浆机开始打碎...
但这样似乎有个问题,如果我们想喝原味不加任何调料的豆浆该怎么办?
钩子方法
大致流程其实都是一样的,只是增加了一层判断。
第一步:创建豆浆模板类
public abstract class SoyaMilk {
//模板方法 , make , 模板方法可以写成final,不让子类去覆盖.
final void make() {
select();
if(customerWantCondiments()){
addCondiments();
}
soak();
beat();
}
//选材料
void select() {
System.out.println("选择新鲜的黄豆...");
}
//添加不同的配料
abstract void addCondiments();
//浸泡
void soak() {
System.out.println("黄豆和配料开始浸泡...");
}
//浸泡
void beat() {
System.out.println("黄豆和配料放到豆浆机开始打碎...");
}
//增加钩子方法,决定是否需要添加配料
boolean customerWantCondiments() {
return true;
}
}
第二步:创建不同口味的豆浆
public class RedBeanSoyaMilk extends SoyaMilk {
@Override
void addCondiments() {
System.out.println("加入最好的红豆");
}
}
public class PeanutSoyaMilk 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();
}
}
运行如下:
---------制作红豆豆浆------
选择新鲜的黄豆...
加入最好的红豆
黄豆和配料开始浸泡...
黄豆和配料放到豆浆机开始打碎...
---------制作花生豆浆------
选择新鲜的黄豆...
加入最好的花生
黄豆和配料开始浸泡...
黄豆和配料放到豆浆机开始打碎...
---------制作纯豆浆------
选择新鲜的黄豆...
黄豆和配料开始浸泡...
黄豆和配料放到豆浆机开始打碎...
优点:
1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。