👉文章示例代码👈
定义
定义一个算法的步骤,并允许子类为一个或者多个步骤提供实现。
模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
场景示例
笔者这里以做番茄炒蛋为例。笔者将做番茄炒蛋的步骤分为以下几步:倒油、放鸡蛋、放番茄、倒调料、翻炒。不考虑多余的细节问题,不同的人做番茄炒蛋的步骤应该是类似的。
创建抽象模板
这里主要定义炒菜的步骤。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
|
public abstract class AbstractCook {
protected final void cook() { this.pourOil(); this.addEgg(); this.addTomato(); this.pourSeasoning(); this.stirFry(); if (needChoppedGreenOnion()) { this.addChoppedGreenOnion(); } }
final void pourOil() { System.out.println("倒入食用油"); }
final void addEgg() { System.out.println("放入鸡蛋"); }
final void addTomato() { System.out.println("放入番茄"); }
final void stirFry() { System.out.println("快速翻炒"); }
final void addChoppedGreenOnion() { System.out.println("放点葱花"); }
protected boolean needChoppedGreenOnion() { return false; }
abstract void pourSeasoning(); }
|
这里的 needChoppedGreenOnion()
是一个钩子方法,让具体的子类来决定是否添加葱花,默认不添加。
以上的部分方法用 final
修饰,是为了防止子类对其进行重写。
创建具体子类
这里以笔者做饭和女朋友做饭为例。由于两人的口味不同,倒入的调味品会存在些许差异。而且笔者喜欢在番茄炒蛋中加入葱花。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
public class MeCook extends AbstractCook {
@Override void pourSeasoning() { System.out.println("放点盐"); System.out.println("放点酱油"); }
@Override protected boolean needChoppedGreenOnion() { return true; } }
public class GirlFriendCook extends AbstractCook {
@Override void pourSeasoning() { System.out.println("放点盐"); } }
|
我们可以看到在 MeCook
类中重写了父类的 needChoppedGreenOnion()
方法,那么对于父类中模板方法执行 needChoppedGreenOnion()
方法时,拿到的其实是子类方法的返回值。
测试类及输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class Test {
public static void main(String[] args) { System.out.println("---我做番茄炒蛋 开始---"); AbstractCook meCook = new MeCook(); meCook.cook(); System.out.println("---我做番茄炒蛋 结束---");
System.out.println("---女朋友做番茄炒蛋 开始---"); AbstractCook girlFriendCook = new GirlFriendCook(); girlFriendCook.cook(); System.out.println("---女朋友做番茄炒蛋 结束---"); } }
|
测试类输出的结果如下:
—我做番茄炒蛋 开始—
倒入食用油
放入鸡蛋
放入番茄
放点盐
放点酱油
快速翻炒
放点葱花
—我做番茄炒蛋 结束—
—女朋友做番茄炒蛋 开始—
倒入食用油
放入鸡蛋
放入番茄
放点盐
快速翻炒
—女朋友做番茄炒蛋 结束—
类结构图
以上示例类的结构图如下所示

总结
适用场景
- 算法的整体步骤固定,但其中的个别部分易变。
- 多个子类存在公共的行为,可以将其提取出来并且集中到一个公共父类当中,避免代码重复。
优点
- 父类提供公共部分代码,提高复用性。
- 将不变部分的算法封装到父类中实现,把可变部分的算法由子类继承去实现,提高了扩展性。
缺点
- 对于每个不同的实现都需要一个子类来实现,导致类数目的增加,增加系统实现的复杂度。
- 父类中的抽象方法由自子类实现,如果父类添加新的抽象方法,所有的子类都需要修改。
扩展
钩子方法:提供缺省行为,子类可以在必要时进行扩展。
参考