- 模板模式:
是类的行为模式, 定义一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法迫使子类实现剩余逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同实现。(其实这种定义生涩难懂了!!!)。
换白话文说就是,系统定义一个抽象类,这个抽象类是用来干嘛的呢?用来定义系统的的步骤,而且这个步骤是不可改变先后顺序的。类似于泡茶,你首先要准备开水,然后冲泡,倒入茶杯中,然后才能喝到茶。这个步骤是前后顺序不可颠倒的,不可变的。这个就是模板模式的模板方法。而这个抽象类中又有对应的方法。烧开水、倒入茶杯,这些动作就相当于模板模式中的基本方法。但是冲泡的时候又不同的冲泡方式,比如不同的时间,不同的茶叶等等,这里面又存在这差异。这些差异就由不同的人来决定,这些人就是这个抽象类的子类,他们集成了抽象类,但是重写了里面的某些方法。 - 模板模式示意图:
通过上图可以看出,模板模式包括以下角色:
- 抽象模板角色: 它定义了一个或多个抽象操作(抽象方法),让子类去实现。这些抽象操作或者是抽象方法叫做基本方法,图中的brew()就是基本方法。他是模板模式中模板方法的组成部分;其次,抽象模板角色中还定义了一个模板方法,也就是图上的prepareDrink();这个模板方法一般是个具体的方法,他给出了一个顶级逻辑,也就是具体的步骤。而将组成这些逻辑的具体实现,放到子类中。当然,这个顶级逻辑也有可能调用具体的方法。
抽象模板角色中一般有以下三种方法: - 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
- 具体方法:由抽象类声明或者实现而子类不进行覆盖。
- 钩子方法:钩子方法呦抽象类声明并实现,而子类会加以扩展,通常抽象类会给出一个空实现。作为方法的默认实现。
- 具体模板角色: 继承抽象模板角色,并且实现抽象模板角色一个或者多个抽象方法。每个抽象模板角色都可能有一个或者多个具体模板角色。而且各个具体模板角色对于抽象模板角色中的基本方法的实现也不相同哟。
在这里还要提一点,在模板模式中还有一个钩子的概念(hook),为什么要加入hook呢,这是为了解决模板方法太过于固定的情况,有个钩子方法可以使得模板方法可以变得灵活,举个栗子:比如说泡茶,有些人喜欢加入一些枸杞之类的东西,但是有些人就只是喝茶,钩子就是为了解决这种情况而存在的。而且对于钩子方法还有一定的命名规则,一般是以do开头,这就可以联想到doGet(),doPost(),方法,确实,我们使用Servlet也是模板模式。
- 示例代码:
场景:泡茶和泡咖啡,包括钩子方法。
抽象模板角色:
public abstract class PrepareDrinK {
private void boilWater(){
System.out.println("烧水了。。。。。");
}
public abstract void brew();
private void pourInCup(){
System.out.println("倒入茶杯中.....");
}
public abstract void drink();
/**
* 钩子方法
* @return
*/
public boolean doCondition(){
return false;
}
public abstract void addCondition();
//抽象模板模式的关键,一定要用关键字final
public final void prepareDrink(){
boilWater();
brew();
pourInCup();
if (doCondition()){
addCondition();
}else {
System.out.println("不需要添加其他东西");
}
}
}
具体模板角色:
泡茶:
public class PrepareTea extends PrepareDrinK {
@Override
public void brew() {
System.out.println(">>>>>>放入茶叶>>>>>>>");
}
@Override
public void drink() {
System.out.println(">>>>>>喝茶>>>>>>>");
}
@Override
public void addCondition() {
System.out.println(">>>>>>加枸杞>>>>>>>");
}
/**
* 重写钩子方法
* @return
*/
@Override
public boolean doCondition() {
System.out.println("您是否需要添加枸杞(y/n):");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String result = "";
try {
result = bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (result.equals("n")){
return false;
}
return true;
}
}
泡咖啡:
public class PrepareCoffee extends PrepareDrinK {
@Override
public void brew() {
System.out.println("放入咖啡粉。。。");
}
@Override
public void drink() {
System.out.println("喝咖啡。。。。。");
}
@Override
public void addCondition() {
System.out.println("加牛奶。。。。");
}
/**
* 重写钩子方法
* @return
*/
@Override
public boolean doCondition() {
System.out.println("您是否需要添加牛奶(y/n):");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String result = "";
try {
result = bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (result.equals("n")){
return false;
}
return true;
}
}
测试主函数:
public class Main {
public static void main(String[] args) {
PrepareDrinK tea = new PrepareTea();
tea.prepareDrink();
PrepareDrinK coffee = new PrepareCoffee();
coffee.prepareDrink();
}
}
- 模板模式的优缺点:
优点:具有良好的分装性和复用性。因为提取了很多的公共代码,因此便于维护。
缺点:由于在JAVA中只支持一重继承,因此每一个不同实现都需要一个新建一个子类,会导致系统的子类过多。 - 说明
模板模式的注意点:
- 模板方法用public final修饰
- 基本方法用private修饰
- 可扩展的方法用abstract修饰