一、动机(Motivate)

“模板方法”,就是有一个方法包含了一个模板,这个模板是一个算法。在我们的现实生活中有很多例子可以拿来说明这个模式,就拿吃饺子这个事情来说,要想吃到饺子必须经过三步,第一步是“和面”,第二步是“包馅”,第三步是“煮饺子”,这三步就是一个算法,我们要想吃到不同的面和馅的饺子,对这三步中的任意一步就行操作就可以,也可以完全定义这三步

在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?

二、意图(Intent)

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。                                 ——《设计模式》GoF

三、结构图

四、模式的组成

模板方法模式参与者:

  (1)、抽象类角色(AbstractClass):定义一个模板方法(TemplateMethod),在该方法中包含着一个算法的骨架,具体的算法步骤是PrimitiveOperation1方法和PrimitiveOperation2方法,该抽象类的子类将重定义PrimitiveOperation1和PrimitiveOperation2操作。

  (2)、具体类角色(ConcreteClass):实现PrimitiveOperation1方法和PrimitiveOperation2方法以完成算法中与特定子类(Client)相关的内容。

  在模板方法模式中,AbstractClass中的TemplateMethod提供了一个标准模板,该模板包含PrimitiveOperation1和PrimitiveOperation2两个方法,这两个方法的内容Client可以根据自己的需要重写。

五、模板方法模式的具体实现

下面以生活中吃饺子为例来实现模板方法模式。在现实生活中,做饺子的步骤都大致相同,如果我们针对每种饺子的做法都定义一个类,这样在每个类中都有很多相同的代码,为了解决这个问题,我们一般的思路肯定是把相同的部分抽象出来到抽象类中去定义,具体子类来实现具体的不同部分,这个思路也正式模板方法的实现精髓所在,具体实现代码如下:

static void Main(string[] args)
{
//现在想吃绿色面的,猪肉大葱馅的饺子
AbstractClass fan = new ConcreteClass1();
fan.EatDumplings(); Console.WriteLine();
//过了段时间,我开始想吃橙色面的,韭菜鸡蛋馅的饺子
fan = new ConcreteClass2();
fan.EatDumplings();
} //该类型就是抽象类角色--AbstractClass,定义做饺子的算法骨架,这里有三步骤,当然也可以有多个步骤,根据实际需要而定
public abstract class AbstractClass
{
//该方法就是模板方法,方法里面包含了做饺子的算法步骤,模板方法可以返回结果,也可以是void类型,视具体情况而定
public void EatDumplings()
{
MakingDough();//和面
MakeDumplings();//包馅
BoiledDumplings(); //煮饺子
Console.WriteLine("饺子真好吃!");
} public abstract void MakingDough();//要想吃饺子第一步肯定是“和面”---该方法相当于算法中的某一步
public abstract void MakeDumplings();//要想吃饺子第二部是“包饺子”---该方法相当于算法中的某一步
public abstract void BoiledDumplings(); //要想吃饺子第三部是“煮饺子”---该方法相当于算法中的某一步
} //该类型是具体类角色--ConcreteClass,我想吃绿色面皮,猪肉大葱馅的饺子
public sealed class ConcreteClass1 : AbstractClass
{
public override void MakingDough() //要想吃饺子第一步肯定是“和面”---该方法相当于算法中的某一步
{
Console.WriteLine("在和面的时候加入芹菜汁,和好的面就是绿色的");//我想要面是绿色的,绿色健康嘛,就可以在此步定制了
} public override void MakeDumplings()//要想吃饺子第二部是“包饺子”---该方法相当于算法中的某一步
{
Console.WriteLine("农家猪肉和农家大葱,制作成馅");//我想吃猪肉大葱馅的,在此步就可以定制了
} public override void BoiledDumplings()//要想吃饺子第三部是“煮饺子”---该方法相当于算法中的某一步
{
Console.WriteLine("用我家的大铁锅和大木材煮饺子");//我想吃大铁锅煮的饺子,有家的味道,在此步就可以定制了
}
} //该类型是具体类角色--ConcreteClass2,我想吃橙色面皮,韭菜鸡蛋馅的饺子
public sealed class ConcreteClass2 : AbstractClass
{
public override void MakingDough()//要想吃饺子第一步肯定是“和面”---该方法相当于算法中的某一步
{
Console.WriteLine("在和面的时候加入胡萝卜汁,和好的面就是橙色的");//我想要面是橙色的,加入胡萝卜汁就可以。在此步定制就可以了。
} public override void MakeDumplings() //要想吃饺子第二部是“包饺子”---该方法相当于算法中的某一步
{
Console.WriteLine("农家鸡蛋和农家韭菜,制作成馅"); //我想吃韭菜鸡蛋馅的,在此步就可以定制了
} public override void BoiledDumplings()//要想吃饺子第三部是“煮饺子”---该方法相当于算法中的某一步
{
Console.WriteLine("可以用一般煤气和不粘锅煮就可以"); //此处没要求
}
}

六、模板方法模式的实现要点:

Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展,是代码复用方面的基本实现结构。除了可以灵活应对子步骤的变化外,“Don't call me, let me call you(不要调用我,让我来调用你)”的反向控制结构是Template Method的典型应用。

1、模板方法模式适用情形:

  (1)、 一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
  (2)、 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
  (3)、 控制子类扩展。模板方法只允许在特定点进行扩展,而模板部分则是稳定的。

2、模板方法模式特点:

  (1)、 TemplateMethod模式是一种非常基础性的设计模式,在面向对象系统中大量应用。它用最简洁的机制(基础、多态)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
  (2)、 在具体实现方面,被TemplateMethod调用的虚方法可以具有实现,也可以没有任何实现(抽象方法或虚方法)。但一般推荐将它们设置为protected方法使得只有子类可以访问它们。
  (3)、 模板方法模式通过对子类的扩展增加新的行为,符合“开闭原则”。

七、.NET 中模板模式的实现

这种模式在控件设计中大量的用到,比如:控件有自己的生命周期,Page对象也有自己的生命周期,Application应用对象也有自己的生命周期,这个生命周期里面的每个阶段其实就是模板方法里面包含的每个步骤,这些阶段步骤会被一个方法包含着,这个方法就是“模板方法”。让我们再说说控件吧,因为写好的控件,可能需要被开发人员自定义,那么在控件里我们已经定义好了控件呈现、动作的骨架,但是有些自定义的需求,需要延迟到扩展控件的开发人员来决定。   
      当我们在做Windows应用程序的时候,就会使用Windows控件,那Windows控件是如何显示在Windows Form 上的呢,就需要一个OnPaint方法把控件画出来,这里OnPaint是一个虚方法的子步骤,这就是一个Template Method设计模式。如果我们不去重写这个OnPaint方法,它就有一个基本的默认实现,画一个空窗体。这里我们并没有调用OnPaint方法,而是Application的Run会进入Windows的消息循环结构,Paint就是一个消息。当我们移动一下窗口都会导致Paint事件的发生,并导致OnPaint函数的调用,这就是一种反向调用。当然,还有很多其他的子步骤可以提供扩展点,例如OnClose等,很多以On开头的全部都是Template Method模式的虚方法。 这个里面内容很复杂,它并不是用一个Template Method在里面调用所有的子步骤方法,它实际上是把整体的Template Method方法置于了一个消息循环的结构里面,我们可以把消息循环的结构看做模板方法里面的TemplateMethod公有非虚方法。

行为型模式(一) 模板方法模式(Template Method)的更多相关文章

  1. 设计模式的征途—17.模板方法(Template Method)模式

    在现实生活中,很多事情都需要经过几个步骤才能完成,例如请客吃饭,无论吃什么,一般都包含:点单.吃东西.买单等几个步骤,通常情况下这几个步骤的次序是:点单=>吃东西=>买单.在这3个步骤中, ...

  2. Android设计模式之命令模式、策略模式、模板方法模式

    命令模式是其它很多行为型模式的基础模式.策略模式是命令模式的一个特例,而策略模式又和模板方法模式都是算法替换的实现,只不过替换的方式不同.下面来谈谈这三个模式. 命令模式 将一个请求封装为一个对象,从 ...

  3. 简介Python设计模式中的代理模式与模板方法模式编程

    简介Python设计模式中的代理模式与模板方法模式编程 这篇文章主要介绍了Python设计模式中的代理模式与模板方法模式编程,文中举了两个简单的代码片段来说明,需要的朋友可以参考下 代理模式 Prox ...

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

    1.场景模拟 使用软件模拟登录控制,普通用户和工作人员用户,工作人员的密码在数据库中是加密的. 步骤大致如下: 前台提交,后台获取登录信息,同数据库中的登陆信息进行比较,只不过工作人员是加密的,普通用 ...

  5. 【java设计模式】之 模板方法(Template Method)模式

    1. 模板方法的一个实例 这一节主要来学习一下设计模式中的模板方法模式.我们先来看一个例子:假如现在老板让你做一个汽车的模型,要求只要完成基本功能即可,不考虑扩展性,那你会怎么做呢?我们首先会根据经验 ...

  6. 编程模式之模板方法模式(Template Method)

    模板方法模式由两个角色组成:父类角色,子类角色. 父类角色:提供模板. 子类角色:为父类模板提供实现. 类图: JAVA代码: AbstractClass.java package com.templ ...

  7. 行为类模式(十):模板方法(Template Method)

    定义 定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤. UML 优点 模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复 ...

  8. 模板方法(Template Method)模式

    /* * 抽象模版(AbstractClass)角色有如下的责任: 定义了一个或多个抽象操作,以便让子类实现.这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤. 定义并实现了一个模版方法.这个模 ...

  9. 设计模式---组件协作模式之模板方法模式(Tempalte Method)

    前提:组件协作模式 现代软件专业分工之后的第一个结构是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常见的模式. 我们常常使用框架来写自己的 ...

随机推荐

  1. mysql 控制流函数

    MySQL有4个函数是用来进行条件操作的,这些函数可以实现SQL的条件逻辑,允许开发者将一些应用程序业务逻辑转换到数据库后台. MySQL控制流函数: CASE WHEN[test1] THEN [r ...

  2. LeetCode 290. 单词规律(Word Pattern) 41

    290. 单词规律 290. Word Pattern 题目描述 给定一种规律 pattern 和一个字符串 str,判断 str 是否遵循相同的规律. 这里的 遵循 指完全匹配,例如,pattern ...

  3. LeetCode 227. 基本计算器 II(Basic Calculator II)

    227. 基本计算器 II 227. Basic Calculator II 题目描述 实现一个基本的计算器来计算一个简单的字符串表达式的值. 字符串表达式仅包含非负整数,+,-,*,/ 四种运算符和 ...

  4. 03 Mybatis框架---学习笔记1--框架的概念及优势

    1.框架的概念 框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统.简单说就是使用别人搭好的舞台,你来做表演.而且,框架一般是成熟的,不断升级的软件.框架是我们软件开发中的一套解决方 ...

  5. [转帖]Linux教程(11)- linux中的计划作业

    Linux教程(11)- linux中的计划作业 2018-08-21 17:13:36 钱婷婷 阅读数 160更多 分类专栏: Linux教程与操作 Linux教程与使用   版权声明:本文为博主原 ...

  6. hdu 1022 Train Problem I【模拟出入栈】

    Train Problem I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)T ...

  7. Go语言 ( 切片)

    本文主要介绍Go语言中切片(slice)及它的基本使用. 引子 因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性. 例如: func arraySum(x []int) in ...

  8. max_prepared_stmt_count参数

    MySQL报错[mysqld-5.5.17-log]Can't create more than max_prepared_stmt_count statements (current value: ...

  9. FIFO形成3x3矩阵

    Verilog生成矩阵一般是使用shift_ip核,但其实用两个FIFO也行.最近刚好学到这种方法,把原理总结一下. 要求 现在有10x5的数据和对应数据有效指示信号,数据为0~49,要用FPGA对其 ...

  10. 同一个Tomcat部署多个springboot项目问题

    2018-12-13 10:37:21,412 ERROR [localhost-startStop-2] c.a.d.s.DruidDataSourceStatManager [DruidDataS ...