Java设计模式之行为型-模板方法模式 (Template Method)

👉文章示例代码👈

附链

你也可以在这些平台阅读本文:

定义

定义一个算法的步骤,并允许子类为一个或者多个步骤提供实现。

模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

场景示例

笔者这里以做番茄炒蛋为例。笔者将做番茄炒蛋的步骤分为以下几步:倒油、放鸡蛋、放番茄、倒调料、翻炒。不考虑多余的细节问题,不同的人做番茄炒蛋的步骤应该是类似的。

创建抽象模板

这里主要定义炒菜的步骤。

/**
 * @author zhh
 * @description 抽象模板类
 * @date 2020-02-27 15:47
 */
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 修饰,是为了防止子类对其进行重写。

创建具体子类

这里以笔者做饭和女朋友做饭为例。由于两人的口味不同,倒入的调味品会存在些许差异。而且笔者喜欢在番茄炒蛋中加入葱花。

/**
 * @author zhh
 * @description 具体子类
 * @date 2020-02-27 16:04
 */
public class MeCook extends AbstractCook {

    @Override
    void pourSeasoning() {
        System.out.println("放点盐");
        System.out.println("放点酱油");
    }

    /**
     * 此处覆盖了父类钩子方法的默认实现
     */
    @Override
    protected boolean needChoppedGreenOnion() {
        return true;
    }
}


/**
 * @author zhh
 * @description 具体子类
 * @date 2020-02-27 16:07
 */
public class GirlFriendCook extends AbstractCook {

    @Override
    void pourSeasoning() {
        System.out.println("放点盐");
    }
}

我们可以看到在 MeCook 类中重写了父类的 needChoppedGreenOnion() 方法,那么对于父类中模板方法执行 needChoppedGreenOnion() 方法时,拿到的其实是子类方法的返回值。

测试类及输出

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-27 16:13
 */
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("---女朋友做番茄炒蛋 结束---");
    }
}

测试类输出的结果如下:

—我做番茄炒蛋 开始—
倒入食用油
放入鸡蛋
放入番茄
放点盐
放点酱油
快速翻炒
放点葱花
—我做番茄炒蛋 结束—
—女朋友做番茄炒蛋 开始—
倒入食用油
放入鸡蛋
放入番茄
放点盐
快速翻炒
—女朋友做番茄炒蛋 结束—

类结构图

以上示例类的结构图如下所示

总结

适用场景

  • 算法的整体步骤固定,但其中的个别部分易变。
  • 多个子类存在公共的行为,可以将其提取出来并且集中到一个公共父类当中,避免代码重复。

优点

  • 父类提供公共部分代码,提高复用性。
  • 将不变部分的算法封装到父类中实现,把可变部分的算法由子类继承去实现,提高了扩展性。

缺点

  • 对于每个不同的实现都需要一个子类来实现,导致类数目的增加,增加系统实现的复杂度。
  • 父类中的抽象方法由自子类实现,如果父类添加新的抽象方法,所有的子类都需要修改。

扩展

钩子方法:提供缺省行为,子类可以在必要时进行扩展。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值