简介
Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式(Template Method Pattern) 实际上是封装了一个固定流程,该流程由几个步骤组成,具体步骤可以由子类进行不同实现,从而让固定的流程产生不同的结果。
模板方法模式 非常简单,其实就是类的继承机制,但它却是一个应用非常广泛的模式。
模板方法模式 本质:抽象封装流程,具体进行实现
主要解决
当完成一个操作具有固定的流程时,由抽象固定流程步骤,具体步骤交给子类进行具体实现(固定的流程,不同的实现)。
优缺点
优点
- 封装不变,扩展可变:父类封装了具体流程以及实现部分不变行为,其它可变行为交由子类进行具体实现;
- 流程由父类控制,子类进行实现:框架流程由父类限定,子类无法更改;子类可以针对流程某些步骤进行具体实现;
缺点
- 抽象规定了行为,具体负责实现,与通常事物的行为相反,会带来理解上的困难(通俗地说,“父类调用了子类方法”);
使用场景
- 多个子类有公有的方法,并且逻辑基本相同时;
- 重要,复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现;
- 重构时,模板方法模式 是一个经常使用的模式,把相同的代码抽取到父类,然后通过钩子函数约束其行为;
模式讲解
首先来看下 模板方法模式 的通用 UML 类图:
模板方法模式从 UML 类图中,我们可以看到,模板方法模式 主要包含两种角色:
- 抽象模板(AbstractClass):抽象模板类,定义了一套算法框架/流程;
- 具体实现(ConcreteClass):具体实现类,对算法框架/流程的某些步骤进行了实现;
以下是 模板方法模式 的通用代码:
class Client {
public static void main(String[] args) {
AbstractClass abc = new ConcreteClassA();
abc.templateMehthod();
abc = new ConcreteClassB();
abc.templateMehthod();
}
// 抽象模板类
static abstract class AbstractClass {
protected void step1() {
System.out.println("AbstractClass:step1");
}
protected void step2() {
System.out.println("AbstractClass:step2");
}
protected void step3() {
System.out.println("AbstractClass:step3");
}
// 声明为final方法,避免子类覆写
public final void templateMehthod() {
this.step1();
this.step2();
this.step3();
}
}
// 具体实现类A
static class ConcreteClassA extends AbstractClass {
@Override
protected void step1() {
System.out.println("ConcreateClassA:step1");
}
}
// 具体实现类B
static class ConcreteClassB extends AbstractClass {
@Override
protected void step2() {
System.out.println("ConcreateClassB:step2");
}
}
}
注:通常把抽象模板类AbstractClass
的模板方法templateMethod
定义成final
类型,避免子类对其覆写,并遵命定义算法结构/流程的语义。
举个例子
例子:小明要炒两道菜:炒豆芽和炒茄子;
分析:炒菜都有固定步骤:洗菜,热锅下油,下菜翻炒,下调料,起锅。由于炒菜流程是固定的,而其中有些步骤对不同的菜而言具备不同的操作,因此可以很好地使用 模板方法模式 完成炒菜过程。
具体代码如下:
class Client {
public static void main(String[] args) {
System.out.println("准备炒豆芽");
CookVegetable cookVegetable = new CookBeanSprout();
cookVegetable.cook();
System.out.println();
System.out.println("准备炒茄子");
cookVegetable = new CookEggplant();
cookVegetable.cook();
}
// 抽象模板类:定义炒菜流程
static abstract class CookVegetable {
protected void wash() {
System.out.println("洗菜");
}
protected void pourOil() {
System.out.println("热油下锅");
}
protected void fry() {
System.out.println("下菜翻炒");
}
// 具体调料由菜决定
protected abstract void pourSauce();
// 具体炒菜流程
public final void cook() {
this.wash();
this.pourOil();
this.fry();
this.pourSauce();
System.out.println("起锅吃菜");
}
}
// 豆芽
static class CookBeanSprout extends CookVegetable {
@Override
protected void pourOil() {
System.out.println("热锅少油");
}
@Override
protected void fry() {
System.out.println("快速翻炒");
}
@Override
protected void pourSauce() {
System.out.println("加盐和少量生抽");
}
}
// 茄子
static class CookEggplant extends CookVegetable {
@Override
protected void wash() {
System.out.println("去除头尾,然后用水洗下");
}
@Override
protected void pourOil() {
System.out.println("热锅多油");
}
@Override
protected void pourSauce() {
System.out.println("加盐和鸡精");
}
}
}
结果如下:
准备炒豆芽
洗菜
热锅少油
快速翻炒
加盐和少量生抽
起锅吃菜
准备炒茄子
去除头尾,然后用水洗下
热锅多油
下菜翻炒
加盐和鸡精
我们根据 模板方法模式,就可以抽象定义炒菜的流程,然后针对不同的菜品,由其子类对某些炒菜步骤进行具体实现,这样就完成了炒菜过程。
网友评论