C#设计模式学习笔记:(13)模板方法模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7837716.html,记录一下学习过程以备后续查用。
一、引言
今天我们要讲行为型设计模式的第一个模式--模板方法模式,先从名字上来看。“模板方法”理解为有一个方法的名字叫“模板方法”,也可以换个理解方法:
有一个方法包含了一个模板,这个模板是一个算法。在我们的现实生活中有很多例子可以说明这个模式,就拿吃饺子这个事情来说,要想吃到饺子必须经过
三步:第一步是“和面”,第二步是“包馅”,第三步是“煮饺子”,这三步就是一个算法。如果想吃到不同类型的饺子,可以上面的三步中的任意一步进行操作,
当然也可以完全定义这三步。下面我们就来看看这个模式的详细介绍吧。
二、模板方法模式介绍
模板方法模式:英文名称--Template Method Pattern;分类--行为型。
2.1、动机(Motivate)
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的
关系)而无法和任务的整体结构同时实现。如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
2.2、意图(Intent)
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
——《设计模式》GoF
2.3、结构图
2.4、模式的组成
模板方法模式参与者:
1)抽象类角色(AbstractClass):定义一个模板方法(TemplateMethod),在该方法中包含着一个算法的骨架,具体的算法步骤是PrimitiveOperation1
方法和PrimitiveOperation2方法,该抽象类的子类将重定义PrimitiveOperation1和PrimitiveOperation2操作。
2)具体类角色(ConcreteClass):实现PrimitiveOperation1方法和PrimitiveOperation2方法以完成算法中与特定子类(Client)相关的内容。
在模板方法模式中,AbstractClass中的TemplateMethod提供了一个标准模板,该模板包含PrimitiveOperation1和PrimitiveOperation2两个方法,这两个方
法的内容Client可以根据自己的需要重写。
2.5、模板方法模式的具体实现
理解了模板方法的定义之后,自然实现模板方法也不是什么难事了,下面以生活中吃饺子为例来实现模板方法模式。在现实生活中,做饺子的步骤都大致
相同,如果我们针对每种饺子的做法都定义一个类,这样在每个类中都有很多相同的代码。为了解决这个问题,我们一般的思路肯定是把相同的部分抽象出
来到抽象类中去定义,具体子类来实现具体的不同部分,这个思路也正是模板方法的实现精髓所在,具体实现代码如下:
class Program
{
/// <summary>
/// 该类型就是抽象类角色--AbstractClass,定义做饺子的算法骨架,这里有三步骤,当然也可以有多个步骤,根据实际需要而定。
/// </summary>
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();
} /// <summary>
/// 该类型是具体类角色--ConcreteClass1,我想吃绿色面皮的猪肉大葱馅的饺子。
/// </summary>
public sealed class ConcreteClass1 : AbstractClass
{
//要想吃饺子第一步肯定是“和面”--该方法相当于算法中的某一步
public override void MakingDough()
{
//我想要面是绿色的,绿色健康嘛,就可以在此步定制了。
Console.WriteLine("在和面的时候加入芹菜汁,和好的面就是绿色的。");
} //要想吃饺子第二部是“包饺子”--该方法相当于算法中的某一步
public override void MakeDumplings()
{
//我想吃猪肉大葱馅的,在此步就可以定制了。
Console.WriteLine("农家猪肉和农家大葱,制作成馅。");
} //要想吃饺子第三部是“煮饺子”--该方法相当于算法中的某一步
public override void BoiledDumplings()
{
//我想吃大铁锅煮的饺子,有家的味道,在此步就可以定制了。
Console.WriteLine("用我家的大铁锅和大木材煮饺子。");
}
} /// <summary>
/// 该类型是具体类角色--ConcreteClass2,我想吃橙色面皮的韭菜鸡蛋馅的饺子。
/// </summary>
public sealed class ConcreteClass2 : AbstractClass
{
//要想吃饺子第一步肯定是“和面”--该方法相当于算法中的某一步
public override void MakingDough()
{
//我想要面是橙色的,在此步定制就可以了。
Console.WriteLine("在和面的时候加入胡萝卜汁,和好的面就是橙色的。");
} //要想吃饺子第二部是“包饺子”--该方法相当于算法中的某一步
public override void MakeDumplings()
{
//我想吃韭菜鸡蛋馅的,在此步定制就可以了。
Console.WriteLine("农家鸡蛋和农家韭菜,制作成馅。");
} //要想吃饺子第三部是“煮饺子”--该方法相当于算法中的某一步
public override void BoiledDumplings()
{
//此处没要求
Console.WriteLine("可以用一般煤气和不粘锅煮就可以。");
}
} static void Main(string[] args)
{
#region 模板方法模式
//现在想吃绿色面皮的猪肉大葱馅的饺子
AbstractClass someone = new ConcreteClass1();
someone.EatDumplings();
Console.WriteLine(); //过了段时间,我开始想吃橙色面皮的韭菜鸡蛋馅的饺子。
someone = new ConcreteClass2();
someone.EatDumplings(); Console.Read();
#endregion
}
}
运行结果如下:
这个模式很简单,备注也很详细,看备注应该差不多了。还有一点就是,模板方法里面的算法步骤,可以有默认实现,也可以没有实现,在C#里面可以是
抽象方法,当然模板方法也可以有返回值,也可以没有返回值。
三、模板方法模式的实现要点
Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提
供了灵活的扩展,是代码复用方面的基本实现结构。除了可以灵活应对子步骤的变化外,“Don't call me, let me call you(不要调用我,让我来调用你)”的反
向控制结构是Template Method的典型应用。
3.1、模板方法模式适用情形
1)一次性实现一个算法的不变部分,并将可变的行为留给子类来实现。
2)各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
3)控制子类扩展。模板方法只允许在特定点进行扩展,而模板部分则是稳定的。
3.2、模板方法模式特点
1)Template Method模式是一种非常基础性的设计模式,在面向对象系统中大量应用。它用最简洁的机制(基础、多态)为很多应用程序框架提供了灵活
的扩展点,是代码复用方面的基本实现结构。
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公有非虚方法。
五、总结
曾经有一个写程序的人说,如果一个人使用面向对象的语言写程序,但是没有用过“模板方法”模式,敢肯定这个人写的程序也绝不是面向对象的,只不过是
使用了面向对象的语言而已。虽然有点严厉和刻薄,但是不无道理。这个模式很简单,可能大家在有意或者无意的情况下已经使用过这个模式了,也许只是不
知道它的名称而已。
C#设计模式学习笔记:(13)模板方法模式的更多相关文章
- 设计模式学习笔记--备忘录(Mamento)模式
写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...
- javascript设计模式(张容铭)学习笔记 - 照猫画虎-模板方法模式
模板方法模式(Template Method):父类中定义一组操作算法骨架,而降一些实现步骤延迟到子类中,使得子类可以不改变父类的算法结构的同时可重新定义算法中某些实现步骤. 项目经理体验了各个页面的 ...
- 设计模式学习笔记-Adapter模式
Adapter模式,就是适配器模式,使两个原本没有关联的类结合一起使用. 平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类 ...
- Java-马士兵设计模式学习笔记-装饰者模式
Java装饰者模式简介 一.假设有一个Worker接口,它有一个doSomething方法,Plumber和Carpenter都实现了Worker接口,代码及关系如下: 1.Worker.java p ...
- 研磨设计模式学习笔记2--外观模式Facade
需求:客户端需要按照需求,执行一个操作,操作包括一个系统中的3个模块(根据配置选择是否全部执行). 外观模式优点: 客户端无需知道系统内部实现,,只需要写好配置文件,控制那些模块执行,简单易用. 外观 ...
- 设计模式学习笔记 1.factory 模式
Factory 模式 用户不关心工厂的具体类型,只知道这是一个工厂就行. 通过工厂的实现推迟到子类里面去来确定工厂的具体类型. 工厂的具体类型来确定生产的具体产品. 同时用户不关心这是一个什么样子的产 ...
- 设计模式学习笔记——Composite 组合模式
用于描述无限层级的复杂对象,类似于描述资源管理器,抽象出每一个层级的共同特点(文件夹和文件,展开事件) 以前描述一个对象,是将整个对象的全部数据都描述清楚,而组合模式通过在对象中定义自己,描述自己的下 ...
- 设计模式学习笔记——Bridge 桥接模式
先说一下我以前对桥接模式的理解:当每个类中都使用到了同样的属性或方法时,应该将他们单独抽象出来,变成这些类的属性和方法(避免重复造轮子),当时的感觉是和三层模型中的model有点单相似,也就是让mod ...
- 设计模式学习笔记——Visitor 访问者模式
1.定义IVisitor接口,确定变化所涉及的方法 2.封装变化类.实现IVisitor接口 3.在实体类的变化方法中传入IVisitor接口,由接口确定使用哪一种变化来实现(封装变化) 4.在使用时 ...
- Java-马士兵设计模式学习笔记-责任链模式-FilterChain功能
一.目标 增加filterchain功能 二.代码 1.Filter.java public interface Filter { public String doFilter(String str) ...
随机推荐
- Java入门 - 语言基础 - 09.循环结构
原文地址:http://www.work100.net/training/java-loop.html 更多教程:光束云 - 免费课程 循环结构 序号 文内章节 视频 1 概述 2 while循环 3 ...
- V模型
V模型是Kevin Forsberg & Harold Mooz在1978年提出的,V模型强调测试在系统工程各个阶段中的作用,并将系统分解和系统集成的过程通过测试彼此关联.V模型从整体上看起来 ...
- 异数OS 2017 DPDK 峰会观后感
1.DPDK in Container 使用虚拟网卡设备技术为每一个容器分配一个IP 网卡适配器(queue).容器技术可以解决虚拟机技术中虚拟机过于臃肿,难于热迁移的问题,可能可以代替美团OVS方案 ...
- svn和 android adt的 eclipse插件更新地址
下边这两个插件的更新地址是每次安装android开发环境时都能用到的,为了方便在这里记录一下. android adt: http://dl-ssl.google.com/android/eclips ...
- python笔记05
python笔记05 数据类型 上个笔记知识点总结: 列表中extend特性:extend,(内部循环,将另外一个列表,字符串.元组添加到extend前的列表中) li.extend(s),将s中元素 ...
- springboot中的那些连接池
hello~各位读者新年好! 回想起前几天在部署springboot项目到正线时,线上环境要求jdk7,可项目是基于jdk8开发的,springboot也是用的springboot2以上的版本,可以说 ...
- Python3基础之内置模块
模块和包 一.定义: 模块:用来从逻辑上组织Python代码(变量,函数,类,逻辑:实现一个功能),本质就是.py结尾的Python文件包:用来从逻辑上组织模块,本质就是一个目录(必须带有一个__in ...
- 曹工说Spring Boot源码(13)-- AspectJ的运行时织入(Load-Time-Weaving),基本内容是讲清楚了(附源码)
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- maven jar 包问题
1. Failure to transfer... 这种错误基本是所需要的 jar 包不存在,或者下载不完整,可去本地仓库查看相关 jar 文件的完整性 解决方案: 删除对应 jar 包,重新下载(删 ...
- C++基类和派生类的析构函数
1.派生类也不能继承基类的析构函数. 2.与构造函数不同的是,在派生类的析构函数中不用显式地调用基类的析构函数,因为每个类只有一个析构函数,编译器知道如何选择,无需程序员干涉. 3.构造函数和虚构函数 ...