1.模板方法模式简介
定义一个操作中算法的骨架,而将这些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
模板方法模式是所有模式中最为常见的几个模式之一,是一种基于继承的代码复用技术,它是一种类行为型模式。
模板方法所代表的行为称为顶级行为,其逻辑称为顶级逻辑。模板方法模式的静态结构图如下所示:
2.主要作用
1. 在不改变模板结构的前提下在子类中重新定义模板中的内容。
2. 提高代码复用性 ,将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。
3. 实现了反向控制, 通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 & 符合“开闭原则”
3.源代码
public abstract class AbstractTemplate {
/** * 模板方法 */
public void templateMethod ( ) {
//调用基本方法
abstractMethod ( );
hookMethod ( );
concreteMethod ( );
}
/**
* 基本方法的声明(由子类实现)
*/
protected abstract void abstractMethod ( );
/**
* 基本方法(空方法)
*/
protected void hookMethod ( ) {
}
/**
* 基本方法(已经实现)
*/
private final void concreteMethod ( ) {
//业务相关的代码
}
}
具体模板角色类,实现了父类所声明的基本方法,abstractMethod ( ) 方法所代表的就是强制子类实现的剩余逻辑,而hookMethod ( ) 方法是可选择实现的逻辑,不是必须实现的。
public class ConcreteTemplate extends AbstractTemplate{
//基本方法的实现
@Override
public void abstractMethod ( ) {
//业务相关的代码
}
//重写父类的方法
@Override
public void hookMethod ( ) {
//业务相关的代码
}
}
4.UML类图 & 组成
5.实例讲解
5.1.创建抽象模板结构(Abstract Class):炒菜的步骤
例子:炒菜==》韭菜鸡蛋&醋溜白菜
public abstract class Abstract Class {
//模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
final void cookProcess ( ) {
//第一步:倒油
this.pourOil ( );
//第二步:热油
this.HeatOil ( );
//第三步:倒蔬菜
this.pourVegetable ( );
//第四步:倒调味料
this.pourSauce ( );
//第五步:翻炒
this.fry();
}
//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
//第一步:倒油是一样的,所以直接实现
void pourOil ( ) {
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
void HeatOil ( ) {
System.out.println ("热油");
}
//第三步:倒蔬菜是不一样的(一个下韭菜,一个是下白菜)
//所以声明为抽象方法,具体由子类实现
abstract void pourVegetable ( );
//第四步:倒配料是不一样的(一个下鸡蛋,一个是下醋)
//所以声明为抽象方法,具体由子类实现
abstract void pourSauce ( );
//第五步:翻炒是一样的,所以直接实现
void fry ( ) {
System.out.println("翻炒到熟");
}
}
5.2.创建具体模板(Concrete Class),即”手撕包菜“和”蒜蓉炒菜心“的具体步骤
//炒韭菜鸡蛋的类
public class ConcreteClass_BaoCai extend Abstract Class{
@Override
public void pourVegetable ( ) {
System.out.println(”下锅的蔬菜是韭菜“);
}
@Override
public void pourSauce ( ) {
System.out.println(”下锅配料是鸡蛋“);
}
//炒蒜蓉菜心的类
public class ConcreteClass_CaiXin extend Abstract Class{
@Override
public void pourVegetable ( ) {
System.out.println(”下锅的蔬菜是白菜“);
}
@Override
public void pourSauce ( ) {
System.out.println(”下锅的酱料是醋“);
}
}
5.3. 客户端调用-炒菜了
public class Template Method{
public static void main(String [ ] args){
//炒 - 韭菜鸡蛋
ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai ( );
BaoCai.cookProcess ( );
//炒 - 醋溜白菜
ConcreteClass_ CaiXin = new ConcreteClass_CaiXin ( );
CaiXin.cookProcess ( );
}
}
5.4. 结果输出
倒油
热油
下锅的蔬菜是韭菜
下锅配料是鸡蛋
翻炒到熟
倒油
热油
下锅的蔬菜是白菜
下锅配料是醋
翻炒到熟
6.优缺点
优点:
提高代码复用性 ,将相同部分的代码放在抽象的父类中
提高了拓展性, 将不同的代码放入不同的子类中,通过对子类的扩展增加新的行为
实现了反向控制, 通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,实现了反向控制 & 符合“开闭原则”
缺点:
引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,从而增加了系统实现的复杂度。
7. 应用场景
一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;
各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复;
控制子类的扩展。