模板方法模式 深藏阁楼爱情的钟 2021-07-24 20:53 450阅读 0赞 # 一 点睛 # 在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。 例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。 **定义:** 定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 # 二 结构 # 模板方法(Template Method)模式包含以下主要角色: * 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。 * 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。 * 基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种: * 抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。 * 具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。 * 钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。 一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。 * 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。 # 三 实战 # 【例】炒菜 炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoZW5ncWl1bWluZw_size_16_color_FFFFFF_t_70] 代码如下: public abstract class AbstractClass { public final void cookProcess() { //第一步:倒油 this.pourOil(); //第二步:热油 this.heatOil(); //第三步:倒蔬菜 this.pourVegetable(); //第四步:倒调味料 this.pourSauce(); //第五步:翻炒 this.fry(); } public void pourOil() { System.out.println("倒油"); } //第二步:热油是一样的,所以直接实现 public void heatOil() { System.out.println("热油"); } //第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心) public abstract void pourVegetable(); //第四步:倒调味料是不一样 public abstract void pourSauce(); //第五步:翻炒是一样的,所以直接实现 public void fry(){ System.out.println("炒啊炒啊炒到熟啊"); } } public class ConcreteClass_BaoCai extends AbstractClass { @Override public void pourVegetable() { System.out.println("下锅的蔬菜是包菜"); } @Override public void pourSauce() { System.out.println("下锅的酱料是辣椒"); } } public class ConcreteClass_CaiXin extends AbstractClass { @Override public void pourVegetable() { System.out.println("下锅的蔬菜是菜心"); } @Override public void pourSauce() { System.out.println("下锅的酱料是蒜蓉"); } } public class Client { public static void main(String[] args) { //炒手撕包菜 ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai(); baoCai.cookProcess(); //炒蒜蓉菜心 ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin(); caiXin.cookProcess(); } } > 注意:为防止恶意操作,一般模板方法都加上 final 关键词。 # 四 测试结果 # 倒油 热油 下锅的蔬菜是包菜 下锅的酱料是辣椒 炒啊炒啊炒到熟啊 # 五 优缺点 # **优点:** * 提高代码复用性 将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。 * 实现了反向控制 通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。 **缺点:** * 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。 * 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。 # 六 适用场景 # * 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。 * 需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。 # 七 JDK源码解析 # InputStream类就使用了模板方法模式。在InputStream类中定义了多个 `read()` 方法,如下: public abstract class InputStream implements Closeable { //抽象方法,要求子类必须重写 public abstract int read() throws IOException; public int read(byte b[]) throws IOException { return read(b, 0, b.length); } public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); //调用了无参的read方法,该方法是每次读取一个字节数据 if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; } } 从上面代码可以看到,无参的 `read()` 方法是抽象方法,要求子类必须实现。而 `read(byte b[])` 方法调用了 `read(byte b[], int off, int len)` 方法,所以在此处重点看的方法是带三个参数的方法。 在该方法中第18行、27行,可以看到调用了无参的抽象的 `read()` 方法。 总结如下: 在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取len个字节数据。具体如何读取一个字节数据呢?由子类实现。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoZW5ncWl1bWluZw_size_16_color_FFFFFF_t_70]: /images/20210724/51c9bb84118246feaa88c657fbb2c062.png
相关 模板方法模式 在微软的WebCast上,李建中老师有个设计模式的系列讲座,其中在讲到模板方法(Template Method)曾说:如果你只想学习一种设计模式就学习模板方法吧。由此可见它使用 我不是女神ヾ/ 2022年09月19日 00:17/ 0 赞/ 9 阅读
相关 模板方法模式 模板方法模式会将模板方法以及不需要随业务场景变化的基本方法放到父类中实现,随业务场景变化的基本方法会被定义为抽象方法,由子类提供真正的实现。 下图展示了模板方法模式的核心类, 野性酷女/ 2022年09月10日 11:24/ 0 赞/ 20 阅读
相关 模板方法模式 模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法的结构的情况下,重新定义算法中的某些步骤。 模板方法模式类图: ![ 小鱼儿/ 2022年06月03日 02:41/ 0 赞/ 41 阅读
相关 模板方法模式 一、描述 模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的 素颜马尾好姑娘i/ 2022年06月03日 01:08/ 0 赞/ 18 阅读
相关 模板方法模式 引入一个例子 ![在这里插入图片描述][70] 比如冲咖啡和冲茶两个过程,加工方法似乎都差不多。可以看成如下过程: 把水煮沸boil()->冲泡brew()->倒 约定不等于承诺〃/ 2022年05月06日 10:38/ 0 赞/ 70 阅读
相关 模板方法模式 最近看书又遇到模板方法模式,具体是在同步器(AQS)的内容上。就顺便再来回顾下。 同步器AbstractQueuedSynchronizer(AQS)是一个抽象类。其中定义了 墨蓝/ 2021年10月18日 11:34/ 0 赞/ 332 阅读
相关 模板方法模式 生活中有很多按步骤才能完成的事,比如我们想进房间,需要先将门打来,然后才能进去,进去之后再把门关上。开门和关门是固定的步骤,而进入房间的步骤则不是固定的,它可以有多种方式,走着 约定不等于承诺〃/ 2021年10月15日 06:45/ 0 赞/ 366 阅读
相关 模板方法模式 什么叫做模板方法模式? 在定义功能时,功能的一部分是确定的但是有一部分是不确定的,而确定的部分在使用不确定的部分。那么这时就将不确定的部分暴露出来,由该类的子类去实现。 清疚/ 2021年09月18日 15:40/ 0 赞/ 398 阅读
相关 模板方法模式 一 点睛 在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些 深藏阁楼爱情的钟/ 2021年07月24日 20:53/ 0 赞/ 451 阅读
相关 模板方法模式 在定义功能时,功能的一部分是确定的,一部分是不确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去,由该类的子类去完成。 抽象类(AbstractCl... 系统管理员/ 2021年05月03日 16:24/ 0 赞/ 425 阅读
还没有评论,来说两句吧...