餃子製作的過程
小帥在外奔波多年,厭倦了程序員的生活,終於決定回老家安定下來,開了個餃子館,天天下餃子。
小帥跟一個大廚苦練餃子製作,自己總結了一下餃子製作的過程:
擀麪皮
包餡
水煮
出鍋
加基本調料
加其他調料(顧客自行添加)
小帥就以這個流程爲模板,在店裏做各種各樣的餃子,恩,生意還挺好的。
普通方法做餃子
小帥發揮程序員的特長,把做餃子的經過用代碼寫了下來,其實很簡單:
製作韭菜餡餃子
/**
* 韭菜餡餃子
*/
public class LeekDumpling {
/**
* 做餃子
*/
public void makeDumpling() {
// 擀麪皮
ganMianPi();
// 包餡
baoXian();
// 水煮
shuiZhu();
// 出鍋
chuGuo();
// 加基本調料
jiaJiBenTiaoLiao();
// 加其他調料
jiaQiTaTiaoLiao();
}
// 擀麪皮
public void ganMianPi() {
System.out.println("擀麪皮");
}
// 包餡
public void baoXian() {
System.out.println("包韭菜餡");
}
// 水煮
public void shuiZhu() {
System.out.println("水煮8分鐘");
}
// 出鍋
public void chuGuo() {
System.out.println("出鍋");
}
// 加基本調料
public void jiaJiBenTiaoLiao() {
System.out.println("加醋");
}
// 加其他調料
public void jiaQiTaTiaoLiao() {
}
}
製作豬肉餡餃子
/**
* 豬肉餡餃子
*/
public class PorkDumpling {
/**
* 做餃子
*/
public void makeDumpling() {
// 擀麪皮
ganMianPi();
// 包餡
baoXian();
// 水煮
shuiZhu();
// 出鍋
chuGuo();
// 加基本調料
jiaJiBenTiaoLiao();
// 加其他調料
jiaQiTaTiaoLiao();
}
// 擀麪皮
public void ganMianPi() {
System.out.println("擀麪皮");
}
// 包餡
public void baoXian() {
System.out.println("包豬肉餡");
}
// 水煮
public void shuiZhu() {
System.out.println("水煮10分鐘");
}
// 出鍋
public void chuGuo() {
System.out.println("出鍋");
}
// 加基本調料
public void jiaJiBenTiaoLiao() {
System.out.println("加醋");
}
// 加其他調料
public void jiaQiTaTiaoLiao() {
System.out.println("加重辣");
}
}
小帥的店裏不僅有韭菜餡餃子,豬肉餡餃子還有牛肉餡,蘑菇餡,海鮮餡餃子等等十幾種呢,各種餃子的製作過程其實都是大同小異,主要是包的餡不一樣,然後就水煮的時間不一樣。
如果每種餃子都各自實現一次所有的代碼,會出現大量的重複代碼,該怎麼改進呢?
模板方法做餃子
其實每種餃子的製作都是按固定的流程製作的,這種情況就非常適合使用模板方法模式。
模板方法模式定義如下:模板方法模式在一個方法中定義一個算法骨架,並將某些步驟推遲到子類中實現。模板方法模式可以讓子類在不改變算法整體結構的情況下,重新定義算法中的某些步驟。
類圖如下:
按照模板方法模式改造一下:
餃子抽象類
public abstract class Dumpling {
/**
* 做餃子
*/
public final void makeDumpling() {
// 擀麪皮
ganMianPi();
// 包餡
baoXian();
// 水煮
shuiZhu();
// 出鍋
chuGuo();
// 加基本調料
jiaJiBenTiaoLiao();
// 鉤子
hook();
}
/**
* 擀麪皮
*/
public void ganMianPi() {
System.out.println("擀麪皮");
}
/**
* 包餡(抽象方法需要在子類中實現)
*/
public abstract void baoXian();
/**
* 水煮(抽象方法需要在子類中實現)
*/
public abstract void shuiZhu();
/**
* 出鍋
*/
public void chuGuo() {
System.out.println("出鍋");
}
/**
* 加基本調料
*/
public void jiaJiBenTiaoLiao() {
System.out.println("加醋");
}
/**
* 鉤子(自定義擴展方法,默認實現爲空)
*/
public void hook() {
}
}
韭菜餡餃子
/**
* 韭菜餡餃子
*/
public class LeekDumpling extends Dumpling{
/**
* 包餡
*/
@Override
public void baoXian() {
System.out.println("包韭菜餡");
}
/**
* 水煮
*/
@Override
public void shuiZhu() {
System.out.println("水煮8分鐘");
}
}
豬肉餡餃子
/**
* 豬肉餡餃子
*/
public class PorkDumpling extends Dumpling{
/**
* 包餡
*/
@Override
public void baoXian() {
System.out.println("包豬肉餡");
}
/**
* 水煮
*/
@Override
public void shuiZhu() {
System.out.println("水煮10分鐘");
}
/**
* 覆蓋hook()方法,加其他調料
*/
@Override
public void hook() {
System.out.println("加重辣");
}
}
製作餃子
public class MakeDumpling {
public static void main(String[] args) {
// 製作韭菜餡餃子
System.out.println("製作韭菜餡餃子");
LeekDumpling leekDumpling = new LeekDumpling();
leekDumpling.makeDumpling();
System.out.println();
// 製作豬肉餡餃子
System.out.println("製作豬肉餡餃子");
PorkDumpling porkDumpling = new PorkDumpling();
porkDumpling.makeDumpling();
}
}
輸出結果
製作韭菜餡餃子
擀麪皮
包韭菜餡
水煮8分鐘
出鍋
加醋
製作豬肉餡餃子
擀麪皮
包豬肉餡
水煮10分鐘
出鍋
加醋
加重辣
餃子的固定制作過程,擀麪皮【ganMianPi()】,出鍋【chuGuo()】, 加基本調料【jiaJiBenTiaoLiao()】每種餃子都是一樣的,所以直接在父類中實現,實現了代碼的複用。
包餡【baoXian()】,水煮【shuiZhu()】每種餃子都不一樣,定義爲抽象方法,具體的操作推遲到子類中實現。
鉤子【hook()】方法比較有意思,在父類中默認實現爲空,如果子類有什麼特殊的操作,父類中沒有定義的,那麼子類就可以覆蓋hook(),實現自己擴展的業務邏輯。
比如加辣椒,不是每個客人都喜歡喫辣的,那麼需要加辣椒的顧客自己覆蓋hook()方法,加上辣椒就行了,不需要加辣椒的顧客就不用重寫hook()方法了。
總結
在模板模式經典的實現中,模板方法定義爲 final,可以避免被子類重寫。
需要子類重寫的方法定義爲 abstract,可以強迫子類去實現。
如果子類需要擴展,可以預留一個hook()的空實現方法,讓需要的子類去重寫。
該模式應用了好萊塢原則:別調用(打電話給)我們,我們會調用(打電話給)你。
換句話說就是:子類你別調用我們(父類),我們(父類)會調用你。
模板模式有兩大作用:複用和擴展。
複用指的是,所有的子類可以複用父類中提供的模板方法的代碼。
擴展指的是,框架通過模板模式提供功能擴展點,也就是鉤子hook(),讓框架用戶可以在不修改框架源碼的情況下,基於擴展點定製化框架的功能。
代碼鏈接
(歡迎關注公衆號:編程我也會)