《Head First 设计模式》:模板方法模式
正文
一、定义
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
要点:
- 模板方法定义了一个算法的步骤,每个步骤都被一个方法所代表,而这几个方法的具体实现可由子类提供。
- 模板方法可确保算法的结构保持不变,同时由子类提供部分实现。
二、实现步骤
1、创建一个抽象类,并定义模板方法
模板方法一般声明为 final,以免子类改变算法的步骤。
抽象类中,可以声明一些钩子方法,子类视情况决定要不要覆盖它们。钩子的存在,可以让子类有能力对算法的不同点进行挂钩,使得模板方法更具有弹性。
/**
 * 抽象类
 */
public abstract class AbstractClass {
    /**
     * 模板方法
     */
    public final void templateMethod() {
        // 公共步骤,由抽象类实现
        commonStep();
        // 依赖于子类的步骤,由子类实现
        step1();
        step2();
        step3();
        // 钩子方法,由子类决定要不要覆盖
        hook();
    }
    /**
     * 步骤1
     */
    public abstract void step1();
    /**
     * 步骤2
     */
    public abstract void step2();
    /**
     * 步骤3
     */
    public abstract void step3();
    /**
     * 公共步骤
     */
    private void commonStep() {
        System.out.println("I'm common step!");
    }
    /**
     * 钩子方法
     */
    public void hook() {
        // 可以空实现,也可以提供默认实现
    }
}
2、创建具体子类,并提供算法步骤的具体实现
(1)具体子类A
/**
 * 具体子类A
 */
public class ConcreteClassA extends AbstractClass{
    @Override
    public void step1() {
        System.out.println("I'm step A1!");
    }
    @Override
    public void step2() {
        System.out.println("I'm step A2!");
    }
    @Override
    public void step3() {
        System.out.println("I'm step A3!");
    }
    @Override
    public void hook() {
        System.out.println("I'm hook step A!");
    }
}
(2)具体子类B
/**
 * 具体子类B
 */
public class ConcreteClassB extends AbstractClass{
    @Override
    public void step1() {
        System.out.println("I'm step B1!");
    }
    @Override
    public void step2() {
        System.out.println("I'm step B2!");
    }
    @Override
    public void step3() {
        System.out.println("I'm step B3!");
    }
}
3、通过使用不同的子类,来确定具体的算法步骤
public class Test {
    public static void main(String[] args) {
        AbstractClass classA = new ConcreteClassA();
        classA.templateMethod();
        System.out.println();
        AbstractClass classB = new ConcreteClassB();
        classB.templateMethod();
    }
}
三、举个栗子
1、背景
《星巴兹咖啡师傅训练手册》规定了,准备星巴兹饮料时,必须精确地遵循下面的冲泡法:
星巴兹咖啡冲泡法:
- 把水煮沸
- 用沸水冲泡咖啡
- 把咖啡倒进杯子
- 加糖和牛奶
星巴兹茶冲泡法:
- 把水煮沸
- 用沸水浸泡茶叶
- 把茶倒进杯子
- 加柠檬
现在,让我们扮演“代码师傅”,写一些代码来创建咖啡和茶。
2、实现
(1)创建咖啡因饮料抽象类,并定义冲泡步骤的方法
/**
 * 咖啡因饮料抽象类
 */
public abstract class CaffeineBeverage {
    /**
     * 准备冲泡法(模板方法)
     */
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }
    /**
     * 冲泡(依赖于子类的步骤)
     */
    abstract void brew();
    /**
     * 添加调料(依赖于子类的步骤)
     */
    abstract void addCondiments();
    /**
     * 烧水(公共步骤)
     */
    void boilWater() {
        System.out.println("Boiling water");
    }
    /**
     * 把饮料倒进杯子(公共步骤)
     */
    void pourInCup() {
        System.out.println("Pouring into cup");
    }
    /**
     * 顾客是否想要添加调料(钩子方法)
     */
    boolean customerWantsCondiments() {
        return true;
    }
}
(2)创建具体的饮料,并提供具体冲泡步骤的实现
/**
 * 咖啡
 */
public class Caffee extends CaffeineBeverage{
    @Override
    void brew() {
        System.out.println("Dripping Coffee through filter");
    }
    @Override
    void addCondiments() {
        System.out.println("Adding Sugar and Milk");
    }
    @Override
    boolean customerWantsCondiments() {
        String answer = askCustomer();
        if (answer.toLowerCase().startsWith("y")) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * 询问顾客
     */
    private String askCustomer() {
        System.out.print("Would you like milk and suger with your coffee (y/n)?");
        String answer = null;
        Scanner scanner = new Scanner(System.in);
        if (scanner.hasNext()) {
            answer = scanner.nextLine();
        }
        scanner.close();
        if (answer == null) {
            return "no";
        }
        return answer;
    }
}
/**
 * 茶
 */
public class Tea extends CaffeineBeverage{
    @Override
    void brew() {
        System.out.println("Steeping the tea");
    }
    @Override
    void addCondiments() {
        System.out.println("Adding Lemon");
    }
}
(3)冲泡饮料
public class Test {
    public static void main(String[] args) {
        // 茶
        System.out.println("\nMaking tea...");
        CaffeineBeverage tea = new Tea();
        tea.prepareRecipe();
        // 咖啡
        System.out.println("\nMaking coffee...");
        CaffeineBeverage caffee = new Caffee();
        caffee.prepareRecipe();
    }
}
《Head First 设计模式》:模板方法模式的更多相关文章
- linkin大话设计模式--模板方法模式
		linkin大话设计模式--模板方法模式 准备一个抽象类,将部分逻辑以具体方法的形式实现,然后申明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不 ... 
- 结合JDK源码看设计模式——模板方法模式
		前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ... 
- 瑞幸咖啡还是星巴克,一杯下午茶让我明白 设计模式--模板方法模式(Template Method Pattern)
		简介 Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template M ... 
- C#设计模式-模板方法模式
		提到模板,大家肯定不免想到生活中的“简历模板”.“论文模板”.“Word中模版文件”等,在现实生活中,模板的概念就是——有一个规定的格式,然后每个人都可以根据自己的需求或情况去更新它,例如简历模板,下 ... 
- java设计模式 模板方法模式Template Method
		设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.毫无疑问,设计模式于己 ... 
- JAVA 设计模式 模板方法模式
		定义 模板方法模式 (Template Method) 定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成. 模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 模 ... 
- 深入浅出设计模式——模板方法模式(Template Method Pattern)
		模式动机 模板方法模式是基于继承的代码复用基本技术,模板方法模式的结构和用法也是面向对象设计的核心之一.在模板方法模式中,可以将相同的代码放在父类中,而将不同的方法实现放在不同的子类中.在模板方法模式 ... 
- 设计模式-模板方法模式(Head First)
		参考书籍:Head First设计模式 什么是模板方法模式 定义:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤. 怎 ... 
- javascript设计模式——模板方法模式
		前面的话 在javascript开发中用到继承的场景其实并不是很多,很多时候喜欢用mix-in的方式给对象扩展属性.但这不代表继承在javascript里没有用武之地,虽然没有真正的类和继承机制,但可 ... 
- C++设计模式——模板方法模式
		模板方法模式 在GOF的<设计模式:可复用面向对象软件的基础>一书中对模板方法模式是这样说的:定义一个操作中的算法骨架,而将一些步骤延迟到子类中.TemplateMethod使得子类可以不 ... 
随机推荐
- asp.net mvc 模拟百度搜索
			页面代码: <td><span>*</span>车牌号码:</td> <td> <div id="search"& ... 
- CF804D Expected diameter of a tree 树的直径 根号分治
			LINK:Expected diameter of a tree 1e5 带根号log 竟然能跑过! 容易想到每次连接两个联通快 快速求出直径 其实是 \(max(D1,D2,f_x+f_y+1)\) ... 
- 最优化算法【牛顿法、拟牛顿法、BFGS算法】
			一.牛顿法 对于优化函数\(f(x)\),在\(x_0\)处泰勒展开, \[f(x)=f(x_0)+f^{'}(x_0)(x-x_0)+o(\Delta x) \] 去其线性部分,忽略高阶无穷小,令\ ... 
- 为什么需要将网站封装为app?
			网站封装为app是一种宝贵的资源,为客户提供稳定的平台,一个网站也是一个有效的工具,用于企业与其客户之间的通信.企业网站用户可以通过他们的笔记本电脑,台式机,平板电脑,智能手机以及带有浏览器设备的 ... 
- MySQL--->存储引擎及图形化工具
			本章目标: 掌握MySQL存储引擎的特点 掌握Navicat图形化工具的使用 了解其他的一些图形化管理工具 1.存储引擎种类: 2. 表级锁和行级锁: 3.常见的引擎: InnoDB 存储引擎 MyI ... 
- Java 集合框架综述,这篇让你吃透!
			一.集合框架图 简化图: 说明:对于以上的框架图有如下几点说明 1.所有集合类都位于java.util包下.Java的集合类主要由两个接口派生而出:Collection和Map,Collection和 ... 
- Dubbo系列之 (二)Registry注册中心-注册(1)
			引导 dubbo的服务的注册与发现,需要通过第三方注册中心来协助完成,目前dubbo支持的注册中心包括 zookeeper,consul,etcd3,eureka,nacas,redis,sofa.这 ... 
- 谁来教我渗透测试——黑客必须掌握的HTML基础(二)
			今天我们继续看看html的学习笔记. 文本标签 标题标签<hn> 将文本设置为标题显示的标签对.设定标题字体大小,n=1(大)~6(小),标题大小一共有6种,也就是从<h1>… ... 
- C/C++中char[]和string的连接/合并
			一: C风格字符串连接 #include <iostream> using namespace std; int main() { const char *str = "hell ... 
- C#LeetCode刷题之#680-验证回文字符串 Ⅱ(Valid Palindrome II)
			问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3961 访问. 给定一个非空字符串 s,最多删除一个字符.判断是否 ... 
