模板方法模式——看看 JDK 和 Spring 是如何優(yōu)雅復(fù)用代碼的
場(chǎng)景問(wèn)題


makingDrinks() 中,且第一步和第三步,完全一樣,沒(méi)必要在子類實(shí)現(xiàn),改進(jìn)如下
makingDrinks() 方法來(lái)處理咖啡和茶的制作,而且我們不希望子類覆蓋這個(gè)方法,所以可以申明為 final,不同的制作步驟,我們希望子類來(lái)提供,必須在父類申明為抽象方法,而第一步和第三步我們不希望子類重寫,所以我們聲明為非抽象方法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();
????????//倒進(jìn)杯子
????????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() 只在父類中進(jìn)行即可,不同的步驟放在子類實(shí)現(xiàn)。認(rèn)識(shí)模板方法
模板方法模式是類的行為模式。準(zhǔn)備一個(gè)抽象類,將部分邏輯以具體方法以及具體構(gòu)造函數(shù)的形式實(shí)現(xiàn),然后聲明一些抽象方法來(lái)迫使子類實(shí)現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實(shí)現(xiàn)這些抽象方法,從而對(duì)剩余的邏輯有不同的實(shí)現(xiàn)。這就是模板方法模式的用意。

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();
????????}
????}
????//定義一個(gè)空的缺省方法,只返回?true
????boolean?customerLike()?{
????????return?true;
????}
}
鉤子:在模板方法的父類中,我們可以定義一個(gè)方法,它默認(rèn)不做任何事,子類可以視情況要不要覆蓋它,該方法稱為“鉤子”。
public?class?Coffee?extends?Drinks?{
????@Override
????void?brew()?{
????????System.out.println("沖咖啡粉");
????}
????@Override
????void?addCondiments()?{
????????System.out.println("加奶加糖");
????}
??//覆蓋了鉤子,提供了自己的詢問(wèn)功能,讓用戶輸入是否需要加料
????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() 方法,之后就可以通過(guò) Collections.sort() 或者 Arrays.sort() 方法進(jìn)行排序了。@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("星冰樂(lè)",38),
??????????????????????new?Coffee("拿鐵",32),
??????????????????????new?Coffee("摩卡",35)};
?
??Arrays.sort(coffees);
??for?(Coffee?coffee1?:?coffees)?{
????System.out.println(coffee1);
??}
}

構(gòu)建對(duì)象數(shù)組 通過(guò) Arrays.sort 方法對(duì)數(shù)組排序,傳參為 Comparable接口的實(shí)例比較時(shí)候會(huì)調(diào)用我們的實(shí)現(xiàn)類的 compareTo()方法將排好序的數(shù)組設(shè)置進(jìn)原數(shù)組中,排序完成
Spring 中的模板方法
AbstractApplicationContext 的 refresh 方法實(shí)現(xiàn)了 IOC 容器啟動(dòng)的主要邏輯。refresh() 方法包含了好多其他步驟方法,像不像我們說(shuō)的 模板方法,getBeanFactory() 、refreshBeanFactory() 是子類必須實(shí)現(xiàn)的抽象方法,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();
??}
?}
????//?兩個(gè)抽象方法
????@Override
?public?abstract?ConfigurableListableBeanFactory?getBeanFactory()?throws???IllegalStateException;?
????
????protected?abstract?void?refreshBeanFactory()?throws?BeansException,?IllegalStateException;
????
????//鉤子方法
????protected?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)?{
?}
?}
ClassPathXmlApplicationContext 和 AnnotationConfigApplicationContext 啟動(dòng)入口,都是它的實(shí)現(xiàn)類(子類的子類的子類的...)。AbstractApplicationContext的一個(gè)子類 AbstractRefreshableWebApplicationContext 中有鉤子方法 onRefresh()的實(shí)現(xiàn):public?abstract?class?AbstractRefreshableWebApplicationContext?extends?……?{
????/**
??*?Initialize?the?theme?capability.
??*/
?@Override
?protected?void?onRefresh()?{
??this.themeSource?=?UiApplicationContextUtils.initThemeSource(this);
?}
}

?
小總結(jié)
參考:
評(píng)論
圖片
表情
