饺子制作的过程
小帅在外奔波多年,厌倦了程序员的生活,终于决定回老家安定下来,开了个饺子馆,天天下饺子。
小帅跟一个大厨苦练饺子制作,自己总结了一下饺子制作的过程:
擀面皮
包馅
水煮
出锅
加基本调料
加其他调料(顾客自行添加)
小帅就以这个流程为模板,在店里做各种各样的饺子,恩,生意还挺好的。
普通方法做饺子
小帅发挥程序员的特长,把做饺子的经过用代码写了下来,其实很简单:
制作韭菜馅饺子
/**
* 韭菜馅饺子
*/
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(),让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
代码链接
(欢迎关注公众号:编程我也会)