说明
知道算法所需的 关键步骤,而且确定了这些步骤的 执行顺序,但 某些步骤的具体实现还未知(因为这些步骤的实现与具体的环境相关)。那么在父类中定义了算法的骨架,相同的操作写在父类中,不相同的部分由子类实现。
模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
理解
上面的算法,可以是:读写文件、甚至包饺子、生产汽车、写小说
包饺子,可以包各钟馅:三鲜馅、牛肉馅、辣椒猪肉。。。。
但 关键步骤 是相同的,而且 顺序固定,如下:
- 买食材(不同馅的饺子,食材不同)
- 剁馅(不同馅的饺子,不相同)
- 和面(相同)
- 包饺子(相同)
- 煮饺子 或 蒸饺子(不同吃法,操作方式不同)
可以在父类中实现:
- 步骤3、4、5的方法
- 定义步骤1、2的抽象方法(由子类实现这两个方法)
- 定义
execute()方法,该方法中调用步骤1、2、3、4、5
不同馅的饺子,实现不同的子类,在子类中实现:步骤1、2
主要解决
一些方法通用,却在每一个子类都重新写了这一方法。
关键代码
具体方法:在抽象父类中实现,代表算法的不变部分
模板方法:在抽象父类中定义,即:上面的 execute() 方法,调用抽象方法和具体方法,确保顺序固定
抽象方法:定义在抽象父类中,由子类实现,代表算法的可变部分。
优点
- 封装不变部分,扩展可变部分。
- 提取公共代码,便于维护。
- 行为由父类控制,子类实现。
缺点
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
应用场景
- 有多个子类共有的方法,且逻辑相同。
- 重要的、复杂的方法,可以考虑作为模板方法。
真实案例
java中的io流,如:FileInputStream 继承了 InputStream 的 mark 、reset 方法
实现
将这些通用算法抽象出来,在抽象类实现,其他步骤在子类实现
例子-反例
包牛肉蒸饺代码
package fanli;
public class NiurouJiaozi {
protected void buy(){
System.out.println("买牛肉");
System.out.println("买大葱");
}
protected void duoxian(){
System.out.println("剁牛肉");
System.out.println("剁大葱");
}
protected void huomian(){
System.out.println("和面");
}
protected void bao(){
System.out.println("包饺子");
}
protected void pengren(){
System.out.println("蒸饺子");
}
public static void main(String[] args) {
NiurouJiaozi jiaozi = new NiurouJiaozi();
// 调用顺序由开发者控制,如果不了解顺序,容易调用出错
jiaozi.buy();
jiaozi.duoxian();
jiaozi.bao();
jiaozi.pengren();
jiaozi.huomian();
}
}
执行结果:
买牛肉
买大葱
剁牛肉
剁大葱
包饺子
蒸饺子
和面 // 和面在最后一步执行,调用顺序错误
缺点:
- 所有的方法都要实现一遍,即使通用的方法:
包饺子、和面 - 调用顺序由开发者控制,如果不了解顺序,容易调用出错
包三鲜水饺代码
package fanli;
public class SanxianJiaozi {
protected void buy(){
System.out.println("买韭菜");
System.out.println("买鸡蛋");
System.out.println("买虾仁");
}
protected void duoxian(){
System.out.println("剁韭菜");
System.out.println("煎鸡蛋");
System.out.println("拌鸡蛋");
System.out.println("剁虾仁");
}
protected void huomian(){
System.out.println("和面");
}
protected void bao(){
System.out.println("包饺子");
}
protected void pengren(){
System.out.println("煮饺子");
}
public static void main(String[] args) {
SanxianJiaozi jiaozi = new SanxianJiaozi();
// 顺序有程序员调用,如果不了解顺序,容易调用出错
jiaozi.buy();
jiaozi.duoxian();
jiaozi.bao();
jiaozi.pengren();
jiaozi.huomian();
}
}
例子-模板模式实现包饺子
抽象父类
和面、包饺子 方法:在抽象父类中实现,代表算法的不变部分execute() 模板方法:在抽象父类中定义,调用抽象方法和具体方法,确保顺序固定买食材、剁馅、烹饪 抽象方法:定义在抽象父类中,由子类实现,代表算法的可变部分
package gaijin;
public abstract class Jiaozi {
protected abstract void buy();
protected abstract void duoxian();
protected void huomian(){
System.out.println("和面");
}
protected void bao(){
System.out.println("包饺子");
}
abstract void pengren();
public void execute(){
buy();
duoxian();
huomian();
bao();
pengren();
}
}
包牛肉蒸饺子类
package gaijin;
public class NiurouJiaozi extends Jiaozi{
@Override
protected void buy(){
System.out.println("买牛肉");
System.out.println("买大葱");
}
@Override
protected void duoxian(){
System.out.println("剁牛肉");
System.out.println("剁大葱");
}
@Override
void pengren() {
System.out.println("煮饺子");
}
public static void main(String[] args) {
NiurouJiaozi jiaozi = new NiurouJiaozi();
jiaozi.execute();
}
}
包三鲜水饺子类
package gaijin;
public class SanxianJiaozi extends Jiaozi{
@Override
protected void buy(){
System.out.println("买韭菜");
System.out.println("买鸡蛋");
System.out.println("买虾仁");
}
@Override
protected void duoxian(){
System.out.println("剁韭菜");
System.out.println("煎鸡蛋");
System.out.println("拌鸡蛋");
System.out.println("剁虾仁");
}
@Override
void pengren() {
System.out.println("煮饺子");
}
protected void zheng(){
System.out.println("煮饺子");
}
public static void main(String[] args) {
SanxianJiaozi jiaozi = new SanxianJiaozi();
jiaozi.execute();
}
}
参考:
https://www.runoob.com/design-pattern/template-pattern.html
https://blog.csdn.net/weixin_45433817/article/details/131355105
https://blog.csdn.net/A1342772/article/details/91349142