假设我们要设计两个类封装咖啡和茶的制作过程:
步骤 | 茶 | 咖啡 |
---|---|---|
步骤一 | 煮热水 | 煮热水 |
步骤二 | 泡茶叶 | 冲咖啡 |
步骤三 | 倒进杯子 | 倒进杯子 |
步骤四 | 加柠檬 | 加牛奶 |
我们发现泡茶和冲咖啡有几个步骤具有相似性,减少代码重复我们需要提炼出一个基类
//饮料制作基类
abstract class BeverageMake{
abstract void make();
final void boilWater(){System.out.println("boilWater");}
final void pourInCup(){System.out.println("pourInCup");}
}
咖啡和茶的类如下方式实现:
//制作咖啡类
class CoffeeMake extends BeverageMake{
@Override
void makeCoffee() {
boilWater();
brewCoffee();
pourInCup();
addMilk();
}
void brewCoffee(){System.out.println("brew Coffee");}
void addMilk(){System.out.println("add Milk");}
}
//制作茶类
class TeaMake extends BeverageMake{
@Override
void makeTea() {
boilWater();
brewTea();
pourInCup();
addLemon();
}
void brewTea(){System.out.println("brew Tea");}
void addLemon(){System.out.println("add Lemon");}
}
尽管已经提炼出了两者相同的部分,但我们还是发现子类实现过程中出现了代码重复,一方面brewCoffee和brewTea尽管在细节上不同,但其处理方式是相似的,应该将其也进行抽象,另一方面不管是茶还是咖啡其制作步骤基本是相同的,不应该将制作方法下放给子类实现,子类很容易因为不了解具体过程而写错处理流程。因此我们需要再次修改基类
//饮料制作基类
abstract class BeverageMake{
final void make(){
boilWater();
brew();
pourInCup();
addCondiments();
}
final void boilWater(){System.out.println("boilWater");}
final void pourInCup(){System.out.println("pourInCup");}
abstract void brew();
abstract void addCondiments();
}
//制作咖啡类
class CoffeeMake extends BeverageMake{
@Override
void brew(){System.out.println("brew Coffee");}
@Override
void addCondiments(){System.out.println("add Milk");}
}
//制作茶类
class TeaMake extends BeverageMake{
@Override
void brew(){System.out.println("brew Tea");}
@Override
void addCondiments(){System.out.println("add Lemon");}
}
这种设计能够最大限度的减少代码重复,而且实现子类时根本不需要关心其实现所有步骤,只关注自身即可。
这就是模板方法模式,模板方法定义了算法的步骤,允许子类修改其中一个或多个步骤。
允许子类修改的方法不一定是抽象方法,基类可以提供一个实现,用于表示默认的处理措施,这样能够有效减少子类的实现难度。这种形式的方法还有另一个名字——“钩子方法”。
模板方法设计模式是框架设计中非常常见的模式,比如Servlet接口的实现,我们只需要修改一个doGet和doPost方法就能够处理浏览器的请求,我们不必精通HTTP请求的具体细节也能处理请求,至于我们的处理什么时候被调用那就是专家们的的事情了。