模板方法仅仅使用了Java的继承机制,但它是应用非常广泛的模式,抽象类的实际应用
模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供具体实现。
模板(模板方法)设计模式:基于抽象类
核心在一个方法定义一个算法的骨架,而将一些步骤延迟到子类中。模板模式可以使得子类在不改变算法的前提下,重新定义算法中的某些步骤。
注意:为了防止恶意操作,一般模板方法都加上final关键字, 不允许被覆写
模板方法的优点:
● 封装不变部分,扩展可变部分
● 提取公共部分代码,便于维护
● 行为由父类控制,子类实现
模板模式就是在模板方法中对基本方法的调用。
模板方法通用代码:
抽象模板类:
public abstract class AbstractClass {
//基本方法
protected abstract void doSomething();
//基本方法
protected abstract void doAnything();
//模板方法
public void templateMethod(){
/*
* 调用基本方法,完成相关的逻辑
*/
this.doAnything();
this.doSomething();
}
}
具体模板类:注意:抽象模板中的基本方法尽量设计为protected类型,符合迪米特法则,不需要暴露的属性或方法尽量不要设置为protected类型。
public class ConcreteClass1 extends AbstractClass {
//实现基本方法
protected void doAnything() {
//业务逻辑处理
}
protected void doSomething() {
//业务逻辑处理
}
}
public class ConcreteClass2 extends AbstractClass {
//实现基本方法
protected void doAnything() {
//业务逻辑处理
}
protected void doSomething() {
//业务逻辑处理
}
}
场景类:
public class Client {
public static void main(String[] args) {
AbstractClass class1 = new ConcreteClass1();
AbstractClass class2 = new ConcreteClass2();
//调用模板方法
class1.templateMethod();
class2.templateMethod();
}
}
模板模式举例·:
星巴克咖啡冲泡法
-
将水煮沸
-
用沸水冲泡咖啡
-
将咖啡倒进杯子
-
加糖和牛奶
星巴克茶冲泡法
-
将水煮沸
-
用沸水浸泡茶叶
-
把茶倒进杯子
-
加柠檬
根据冲泡步骤使用代码实现咖啡和茶的类:
class Coffee{
//咖啡冲泡法(算法)
void prepareRecipe(){
boilWater();
brewCoffeeGrings();
pourInCup();
addSugarAndMilk();
}
public void boilWater(){
System.out.println("将水煮沸");
}
public void brewCoffeeGrings(){
System.out.println("冲泡咖啡");
}
public void pourInCup(){
System.out.println("将咖啡倒进杯子中");
}
public void addSugarAndMilk(){
System.out.println("加糖和牛奶");
}
}
class Tea{
void prepareRecipe(){
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boilWater(){
System.out.println("将水煮沸");
}
public void steepTeaBag(){
System.out.println("浸泡茶");
}
public void pourInCup(){
System.out.println("将茶倒进杯子中");
}
public void addLemon(){
System.out.println("加柠檬");
}
}
public class Test{
public static void main(String[] args) {
Coffee coffee=new Coffee();
coffee.prepareRecipe();
Tea tea=new Tea();
tea.prepareRecipe();
}
}
我们可以发现茶和咖啡非常相似,因此可以将二者相同的部分抽取出来,放进一个基类(父类)当中。
我们可以把“将水煮沸”和“将饮料倒进杯子中”放到一个基类中。
我们在可以从冲泡法和往饮料中加调料着手,将二者的冲泡算法合并成
-
将水煮沸
-
用热水泡饮料
-
将饮料倒进杯子
-
在饮料内加入适当的调料
抽象基类代码实现
//咖啡因饮料是一个抽象类
//抽象模板类
abstract class CaffeineBeverage{
//使用同一个prepareBeverage()方法处理茶和咖啡
//使用final关键字来防止子类覆盖这个方法
//模板方法
final void prepareRecipe(){
boilWater();
//冲饮料的方法
brew();
pourInCup();
//往饮料中加的调料
addCondiments();
}
//基本方法
public void boilWater(){
System.out.println("将水烧开");
}
//基本方法
public void pourInCup(){
System.out.println("将饮料倒进杯子");
}
//将咖啡和茶不同的处理方法声明为抽象,留给子类实现
//基本方法
public abstract void brew();
//基本方法
public abstract void addCondiments();
}
//具体模板类
class Coffee extends CaffeineBeverage{
public void brew(){
System.out.println("冲泡咖啡");
}
public void addCondiments(){
System.out.println("加糖和牛奶");
}
}
//具体模板类
class Tea extends CaffeineBeverage{
public void brew(){
System.out.println("冲泡茶");
}
public void addCondiments(){
System.out.println("加柠檬");
}
}
//场景类
public class Test{
public static void main(String[] args) {
//向上转型
CaffeineBeverage coffee=new Coffee();
coffee.prepareRecipe();
//向上转型
CaffeineBeverage tea=new Tea();
tea.prepareRecipe();
}
}
此时的类图:
模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供具体方法
使用模块方法所带来的优势
不好的茶或咖啡实现 | 模板方法提供的咖啡因饮料 |
Coffee或Tea主导一切,控制算法 | 由超类主导一切,它拥有算法,并且保护这个算法 |
Coffee与Tea之间存在重复代 | 有超类的存在,因此可以将代码复用最大化 |
对于算法所做的代码改变,需要打开各个子类修改很多地方 | 算法只存在一个地方,容易修改 |
弹性差,新种类的饮料加入需要做很多工作 | 弹性高,新饮料的加入只需要实现自己的冲泡和加料方 法即可 |
算法的知识和它的实现分散在许多类中 | 超类专注于算法本身,而由子类提供完整的实现。 |
钩子方法:是一类“默认不做事的方法”,子类可以选择性的覆盖他们
范例:扩展上述类,引入钩子方法
//咖啡因饮料是一个抽象类
abstract class CaffeineBeverage{
//使用同一个prepareBeverage()方法处理茶和咖啡
//使用final关键字来防止子类覆盖这个方法
final void prepareRecipe(){
boilWater();
//冲饮料的方法
brew();
pourInCup();
//判断顾客是否需要加入调料
if(customerWantCondiments()){
//往饮料中加的调料
addCondiments();
}
}
public void boilWater(){
System.out.println("将水烧开");
}
public void pourInCup(){
System.out.println("将饮料倒进杯子");
}
//将咖啡和茶不同的处理方法声明为抽象,留给子类实现
public abstract void brew();
public abstract void addCondiments();
//钩子方法
boolean customerWantCondiments(){
return true;
}
}
class Coffee extends CaffeineBeverage{
public void brew(){
System.out.println("冲泡咖啡");
}
public void addCondiments(){
System.out.println("加糖和牛奶");
}
}
class Tea extends CaffeineBeverage{
public void brew(){
System.out.println("冲泡茶");
}
public void addCondiments(){
System.out.println("加柠檬");
}
public boolean customerWantCondiments(){
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;
}
}
public class Test{
public static void main(String[] args) {
//向上转型
CaffeineBeverage coffee=new Coffee();
coffee.prepareRecipe();
//向上转型
CaffeineBeverage tea=new Tea();
tea.prepareRecipe();
}
}