模板方法模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重新定义该算法的某些特定步骤。
⭐结构
-
抽象类——负责给出一个算法的轮廓和骨架,它由一个模板方法和若干个基本方法构成。
-
模板方法——定义了算法的骨架,按某种顺序调用其包含的基本方法
-
基本方法——是实现算法各个步骤的方法,是模板方法的组成部分。可以分为三种:
- 抽象方法——由抽象类声明,具体子类实现
- 具体方法——由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承
- 钩子方法——在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXXX,返回值类型为boolean类型
-
具体子类——实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
-
🌰举个例子
例如炒菜,倒油、热油、放菜、到调料品等步骤都是固定的,我们可以通过模板方法模式来用代码模拟
设计类图如下:
开始实现:
抽象类中定义模板方法和抽象方法(定义实现的步骤):
public abstract class AbstractClass {
//模板方法,因为步骤不能让子类改动,所以定义为final
public final void cookProcess() {
pourOil();
heatOil();
pourVegetable();
pourSauce();
fry();
}
//第一步,倒油,因为倒油是一样的,所以直接实现
public void pourOil() {
System.out.println("倒油");
}
//第二步,热油,因为热油也是一样的,所以直接实现
public void heatOil() {
System.out.println("热油");
}
//第三步,下菜,因为菜不同,所以不能直接写死,所以定义一个抽象方法
public abstract void pourVegetable();
//第四步,倒调料,不同的菜用不同的调料,所以依旧定义成抽象方法
public abstract void pourSauce();
//第五步,翻炒,直接实现
public void fry() {
System.out.println("我炒我炒我炒");
}
}
定义炒包菜的类:
public class ConcreteClass_BaoCai extends AbstractClass {
public void pourVegetable() {
System.out.println("放包菜");
}
public void pourSauce() {
System.out.println("放辣椒")
}
}
炒菜芯的类:
public class ConcreteClass_BaoCai extends AbstractClass {
public void pourVegetable() {
System.out.println("放菜芯");
}
public void pourSauce() {
System.out.println("放蒜蓉")
}
}
然后我们就能在测试类中调用方法炒菜啦!
先炒个包菜:
ConcreteClass_BaoCai baocai = new ConcreteClass_BaoCai();//创建对象
baoCai.cookProcess();//调用炒菜方法
包菜类就会帮我们按序执行炒菜方法!
⭐优缺点
优点:
- 提高代码复用性——将相同部分的代码放在抽象的父类中,将不同的代码放入不同的子类中
- 实现了反向控制——通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制,并符合“开闭原则”
缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
⭐适用场景
- 算法的整体步骤是固定的,但其中个别部分易变时,就可以使用模板方法模式,将容易变的部分抽象出来,供子类实现
- 需要通过子类来决定父类算法中的某个步骤是否执行,实现子类对父类的反向控制