模板方法模式——看看 JDK 和 Spring 是如何优雅复用代码的
场景问题
makingDrinks()
中,且第一步和第三步,完全一样,没必要在子类实现,改进如下makingDrinks()
方法来处理咖啡和茶的制作,而且我们不希望子类覆盖这个方法,所以可以申明为 final,不同的制作步骤,我们希望子类来提供,必须在父类申明为抽象方法,而第一步和第三步我们不希望子类重写,所以我们声明为非抽象方法public abstract class Drinks {
void boilWater() {
System.out.println("将水煮沸");
}
abstract void brew();
void pourInCup() {
System.out.println("倒入杯子");
}
abstract void addCondiments();
public final void makingDrinks() {
//热水
boilWater();
//冲泡
brew();
//倒进杯子
pourInCup();
//加料
addCondiments();
}
}
public class Tea extends Drinks {
@Override
void brew() {
System.out.println("冲茶叶");
}
@Override
void addCondiments() {
System.out.println("加柠檬片");
}
}
public class Coffee extends Drinks {
@Override
void brew() {
System.out.println("冲咖啡粉");
}
@Override
void addCondiments() {
System.out.println("加奶加糖");
}
}
public static void main(String[] args) {
Drinks coffee = new Coffee();
coffee.makingDrinks();
System.out.println();
Drinks tea = new Tea();
tea.makingDrinks();
}
makingDrinks()
就是模板方法。我们可以看到相同的步骤 boilWater()
和 pourInCup()
只在父类中进行即可,不同的步骤放在子类实现。认识模板方法
模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
public abstract class Drinks {
void boilWater() {
System.out.println("将水煮沸");
}
abstract void brew();
void pourInCup() {
System.out.println("倒入杯子");
}
abstract void addCondiments();
public final void makingDrinks() {
boilWater();
brew();
pourInCup();
//如果顾客需要,才加料
if (customerLike()) {
addCondiments();
}
}
//定义一个空的缺省方法,只返回 true
boolean customerLike() {
return true;
}
}
钩子:在模板方法的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
public class Coffee extends Drinks {
@Override
void brew() {
System.out.println("冲咖啡粉");
}
@Override
void addCondiments() {
System.out.println("加奶加糖");
}
//覆盖了钩子,提供了自己的询问功能,让用户输入是否需要加料
boolean customerLike() {
String answer = getUserInput();
if (answer.toLowerCase().startsWith("y")) {
return true;
} else {
return false;
}
}
//处理用户的输入
private String getUserInput() {
String answer = null;
System.out.println("您想要加奶加糖吗?输入 YES 或 NO");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
answer = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (answer == null) {
return "no";
}
return answer;
}
}
JDK 中的模板方法
compareTo()
方法,之后就可以通过 Collections.sort()
或者 Arrays.sort()
方法进行排序了。@Override
public int compareTo(Object o) {
Coffee coffee = (Coffee) o;
if(this.price < (coffee.price)){
return -1;
}else if(this.price == coffee.price){
return 0;
}else{
return 1;
}
}
public static void main(String[] args) {
Coffee[] coffees = {new Coffee("星冰乐",38),
new Coffee("拿铁",32),
new Coffee("摩卡",35)};
Arrays.sort(coffees);
for (Coffee coffee1 : coffees) {
System.out.println(coffee1);
}
}
构建对象数组 通过 Arrays.sort 方法对数组排序,传参为 Comparable
接口的实例比较时候会调用我们的实现类的 compareTo()
方法将排好序的数组设置进原数组中,排序完成
Spring 中的模板方法
AbstractApplicationContext
的 refresh
方法实现了 IOC 容器启动的主要逻辑。refresh()
方法包含了好多其他步骤方法,像不像我们说的 模板方法,getBeanFactory()
、refreshBeanFactory()
是子类必须实现的抽象方法,postProcessBeanFactory()
是钩子方法。public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
}
// 两个抽象方法
@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
//钩子方法
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
}
ClassPathXmlApplicationContext
和 AnnotationConfigApplicationContext
启动入口,都是它的实现类(子类的子类的子类的...)。AbstractApplicationContext
的一个子类 AbstractRefreshableWebApplicationContext
中有钩子方法 onRefresh()
的实现:public abstract class AbstractRefreshableWebApplicationContext extends …… {
/**
* Initialize the theme capability.
*/
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
}
小总结
参考:
评论