模板模式便是通过定义一个模板(结构、框架、原型),在之后的工作便是对其进行充实、完善实际所需。
模板采用抽象类来定义,公共的结构化逻辑需要在抽象基类中定义,只将非公共类的部分逻辑抽象成方法,留在子类充实实现。
用生活中的两个例子来说:泡茶和煮咖啡。
泡茶步骤:1.将水煮沸 2.浸泡茶包 3.将茶倒入杯中 4.加柠檬
煮咖啡步骤:1.将水煮沸 2.冲泡咖啡 3.将咖啡倒入杯中 4.加糖和牛奶
从上面步骤可以看出无论是泡茶还是煮咖啡过程都是大致相似的,甚至1、2步骤是完全相同的,而3、4步骤只是冲泡和加入的调料有所不同,所以根据模板模式的定义,将1、2相同的这两部分放在超类中,可以被两个子类所共用,将步骤3、4声明为抽象方法,留给子类充实。回过头来我们仔细想想既然整个过程大致相似,只是放入的原料以及加入的辅料不同,则我们亦可以将整个过程抽象化。
其类图如下:
代码如下:
实现抽象基类,为所有子类提供模板
import java.util.Scanner;
abstract class CaffeineBeverage{
//冲泡咖啡
//用同一个prepareRecipe()方法处理茶和咖啡。
//声明为final是因为不希望子类覆盖方法!
final void prepareRecipe(){
boilWater();
brew();
pourIncup();
addCondiments();
}
//处理方法不同的,声明为抽象方法,留给子类实现
abstract void brew();
abstract void addCondiments();
public void boilWater(){
System.out.println("将水煮沸");
}
public void pourIncup(){
System.out.println("将饮料倒进杯子中");
}
}
其中brew()与addCondiments()需要具体在子类中实现,需声明为抽象方法。
抽象方法具体在咖啡和茶子类中的实现
//茶子类
class Tea extends CaffeineBeverage{
void brew(){
System.out.println("泡茶");
}
void addCondiments(){
System.out.println("加柠檬");
}
}
//咖啡子类
class Coffee extends CaffeineBeverage{
void brew(){
System.out.println("泡咖啡");
}
void addCondiments(){
System.out.println("加糖和牛奶");
}
在实现第四步骤时,因为在选择添加调料时每个人都有自己的喜好,那么在实现这一个性化需求时,需要提供一个钩子函数(默认不做事的方法,子类视情况决定要不要覆盖),具体到每个场景,使用不同的钩子函数 。
钩子函数的具体实现
//if条件判断钩子函数的调用
if(customerWantsaddCondiments()){
addCondiments();
}
boolean customerWantsaddCondiments(){
return true;
}
public boolean customerWantsaddCondiments(){
String answer = getUserInput();
if(answer.equals("y")){
return true;
}else{
return false;
}
private String getUserInput(){
String answer = null;
System.out.println("您想要加牛奶和糖吗(y/n)");
Scanner scanner = new Scanner(System.in);
answer = scanner.nextLine();
return answer;
}
测试类
private String getUserInput(){
String answer = null;
System.out.println("您想要加牛奶和糖吗(y/n)");
Scanner scanner = new Scanner(System.in);
answer = scanner.nextLine();
return answer;
}
究竟模板方法能带给我们什么呢?