今天我们来学习23种设计模式中的模板模式。模板模式需要注意抽象类与具体子类之间的协作。它用到了虚函数的多态性技术以及“不用调用我,让我来调用你”的反向控制技术。
概念:
模板模式是定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
特点:
优点:
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 它在父类中提取了公共的部分代码,便于代码复用。
- 部分方法是由子类实现的,因此子类可以通过扩展方法增加相应的功能,符合开闭原则。
缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
类图分析:
代码:
抽象类,豆浆
package cn.ppdxzz.template;
/**
* description:抽象类,豆浆
* @author: PeiChen JavaAnything
*/
public abstract class BaseSoyMilk {
/**
* 模板方法,修饰符设置成 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("第四步:黄豆和配料放豆浆机里打碎。");
}
}
红豆牛奶
package cn.ppdxzz.template;
/**
* description:红豆牛奶
* @author: PeiChen JavaAnything
*/
public class RedBeanSoyMilk extends BaseSoyMilk {
@Override
void addCondiments() {
System.out.println("第二步:加入红豆...");
}
}
花生牛奶
package cn.ppdxzz.template;
/**
* description:花生牛奶
* @author: PeiChen JavaAnything
*/
public class PeanutSoyMilk extends BaseSoyMilk {
@Override
void addCondiments() {
System.out.println("第二步:加入花生...");
}
}
模板模式客户端
package cn.ppdxzz.template;
/**
* description:模板模式客户端
* @author: PeiChen JavaAnything
*/
public class Client {
public static void main(String[] args) {
System.out.println("---制作红豆豆浆---");
BaseSoyMilk redBeanSoyMilk = new RedBeanSoyMilk();
redBeanSoyMilk.make();
System.out.println("---制作花生豆浆---");
BaseSoyMilk peanutSoyMilk = new PeanutSoyMilk();
peanutSoyMilk.make();
}
}
运行结果:
总结:
- 有多个子类共有的方法,且逻辑相同时,可以考虑使用模板模式。
- 重要的、复杂的方法,可以考虑作为模板方法。
- 为防止恶意操作,一般模板方法都加上 final 关键词。
Spring 的 IoC 容器在初始化时就是用到了模板模式,需要深入了解的同学可以剖析下源码,自己研究一下。