开闭原则(OCP):一个软件实体如类、模块和函数应该对扩展开放、对修改关闭。
讲模板设计模式之前,我们先来看一下星巴克咖啡冲泡师傅的训练手册。
星巴克咖啡冲泡法
- 将水煮沸
- 用沸水冲泡咖啡
- 将咖啡倒进杯子
- 加糖和牛奶
星巴克茶冲泡法
- 将水煮沸
- 用沸水浸泡茶叶
- 把茶倒进杯子
- 加柠檬
如果将这两个泡法分别写个类来实现的话,我们发现会有很多代码重复。既然茶和咖啡是如此相似,因此我们应该将公同的部分抽取出来,放进一个基类中。
模板设计模式
模板方法定义了一个算法的步骤,并允许子类为一个或者多个步骤提供具体实现。
范例:模板设计模式的实现
/**
* 咖啡因饮料是一个抽象类
*/
abstract class CaffeineBeverage {
/**
* 现在用同一个prepareRecipe()方法处理茶和咖啡。
* 声明为final的原因是我们不希望子类覆盖这个方法!
*/
final void prepareRecipe() {
boilWater();//将水煮沸
brew();//用热水泡饮料
pourInCup();//将饮料倒进杯子里
addCondiments();//在饮料中假如适当的调料
}
/**
* 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现
*/
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("Boiling water");
}
void pourInCup() {
System.out.println("Pouring into cup");
}
}
class Tea extends CaffeineBeverage {
void brew() {
System.out.println("Steeping the tea");
}
void addCondiments() {
System.out.println("Adding Lemon");
}
}
class Coffee extends CaffeineBeverage {
void brew() {
System.out.println("Dripping Coffee through filter");
}
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
public class Interview {
public static void main(String[] args) {
CaffeineBeverage caffeineBeverage=new Tea();
caffeineBeverage.prepareRecipe();
CaffeineBeverage caffeineBeverage1=new Coffee();
caffeineBeverage1.prepareRecipe();
}
}
模板设计模式(优化)
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
import java.util.Scanner;
abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
// 如果顾客想要饮料我们才调用加料方法
if (customerWantsCondiments()){
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("Boiling water");
}
void pourInCup() {
System.out.println("Pouring into cup");
}
/**
* 钩子方法
* 超类中通常是默认实现
* 子类可以选择性的覆写此方法
* @return
*/
boolean customerWantsCondiments() {
return true;
}
}
class Tea extends CaffeineBeverage {
void brew() {
System.out.println("Steeping the tea");
}
void addCondiments() {
System.out.println("Adding Lemon");
}
}
class Coffee extends CaffeineBeverage {
void brew() {
System.out.println("Dripping Coffee through filter");
}
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
/**
* 子类覆写了钩子函数,实现自定义功能
* @return
*/
public boolean customerWantsCondiments() {
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 Interview {
public static void main(String[] args) {
CaffeineBeverage caffeineBeverage=new Tea();
caffeineBeverage.prepareRecipe();
CaffeineBeverage caffeineBeverage1=new Coffee();
caffeineBeverage1.prepareRecipe();
}
}
模板设计模式的优缺点
优点
优点
(1)具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
(2)代码复用的基本技术,在数据库设计中尤为重要。
(3)存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。
缺点
每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。