31模板方法(Template Method)

无处不在的Template Method 
    如果你只想掌握一种设计模式,那么它就是Template Method!
动机(Motivate):
    变化 -----是软件设计的永恒主题,如何管理变化带来的复杂性?设计模式的艺术性和复杂度就在于如何
分析,并发现系统中的变化和稳定点,并使用特定的设计方法来应对这种变化。
意图(Intent):
    定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。                                                        -------《设计模式》GOF
结构图(Struct):
                    
适用性:
    

1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

2.各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。

3.控制子类扩展。模板方法只在特定点调用“Hook”操作,这样就只允许在这些点进行扩展。
生活中的例子:
                
代码实现:

    假如我们需要简单的读取Northwind数据库中的表的记录并显示出来。对于数据库操作,我们知道不管读取的是哪张表,它一般都应该经过如下这样的几步:

1.连接数据库(Connect)

2.执行查询命令(Select)

3.显示数据(Display)

4.断开数据库连接(Disconnect)

这些步骤是固定的,但是对于每一张具体的数据表所执行的查询却是不一样的。显然这需要一个抽象角色,给出顶级行为的实现。如下图:                                                                                                   
                         
Template Method模式的实现方法是从上到下,我们首先给出顶级框架DataAccessObject的实现逻辑:

 1 public abstract class DataAccessObject
 2 
 3 {
 4     protected string connectionString;
 5 
 6     protected DataSet dataSet;
 7 
 8     protected virtual void Connect()
 9 
10     { 
11         connectionString = 
12 
13             "Server=.;User Id=sa;Password=;Database=Northwind";
14 
15     }
16 
17    protected  abstract void Select();
18 
19     protected abstract void Display();
20 
21 
22     protected virtual void Disconnect()
23 
24     {
25         connectionString = "";
26     }
27 
28     // The "Template Method" 
29 
30     public void Run()
31 
32     {
33         Connect();
34 
35         Select();
36 
37         Display();
38 
39         Disconnect();
40     }
41 }

显然在这个顶级的框架DataAccessObject中给出了固定的轮廓,方法Run()便是模版方法,Template Method模式也由此而得名。而对于Select()和Display()这两个抽象方法则留给具体的子类去实现,如下图:
                    

 1 class Categories : DataAccessObject
 2 
 3 {
 4     protected override void Select()
 5     {
 6         string sql = "select CategoryName from Categories";
 7 
 8         SqlDataAdapter dataAdapter = new SqlDataAdapter(
 9 
10             sql, connectionString);
11 
12         dataSet = new DataSet();
13 
14         dataAdapter.Fill(dataSet, "Categories");
15 
16     }
17 
18     protected override void Display()
19 
20     {
21 
22         Console.WriteLine("Categories ---- ");
23 
24         DataTable dataTable = dataSet.Tables["Categories"];
25 
26         foreach (DataRow row in dataTable.Rows)
27 
28         {
29 
30             Console.WriteLine(row["CategoryName"].ToString());
31 
32         }
33 
34         Console.WriteLine();
35 
36     }
37 }

 

 1 class Products : DataAccessObject
 2 
 3 {
 4     protected override void Select()
 5 
 6     {
 7         string sql = "select top 10 ProductName from Products";
 8 
 9         SqlDataAdapter dataAdapter = new SqlDataAdapter(
10 
11             sql, connectionString);
12 
13         dataSet = new DataSet();
14 
15         dataAdapter.Fill(dataSet, "Products");
16 
17     }
18 
19     protected override void Display()
20 
21     {
22 
23         Console.WriteLine("Products ---- ");
24 
25         DataTable dataTable = dataSet.Tables["Products"];
26 
27         foreach (DataRow row in dataTable.Rows)
28 
29         {
30             Console.WriteLine(row["ProductName"].ToString());
31 
32         }
33 
34         Console.WriteLine();
35 
36     }
37 
38 }


再来看看客户端程序的调用,不需要再去调用每一个步骤的方法:

 1 public class App
 2 
 3 {
 4     static void Main()
 5     {
 6 
 7         DataAccessObject dao;
 8 
 9 
10         dao = new Categories();
11 
12         dao.Run();
13 
14 
15         dao = new Products();
16 
17         dao.Run();
18 
19         // Wait for user 
20 
21         Console.Read();
22 
23     }
24 
25 }

 

在上面的例子中,需要注意的是:

1.对于Connect()和Disconnect()方法实现为了virtual,而Select()和Display()方法则为abstract,这是因为如果这个方法有默认的实现,则实现为virtual,否则为abstract。

2.Run()方法作为一个模版方法,它的一个重要特征是:在基类里定义,而且不能够被派生类更改。有时候它是私有方法(private method),但实际上它经常被声明为protected。它通过调用其它的基类方法(覆写过的)来工作,但它经常是作为初始化过程的一部分被调用的,这样就没必要让客户端程序员能够直接调用它了。

3.在一开始我们提到了不管读的是哪张数据表,它们都有共同的操作步骤,即共同点。因此可以说Template Method模式的一个特征就是剥离共同点。
Template Mehtod实现要点:

1.Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。

2.除了可以灵活应对子步骤的变化外,“不用调用我,让我来调用你(Don't call me ,let me call you)”的反向控制结构是Template Method的典型应用。“Don’t call me.Let me call you”是指一个父类调用一个子类的操作,而不是相反。

3.在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法,纯虚方法),但一般推荐将它们设置为protected方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bat 脚本清空窗口内容_tomcat9.0启动脚本startup.bat的分析

1、 Apache Tomcat的下载和安装从Apache官网https://tomcat.apache.org/可以下载各种版本的tomcat软件,下载的文件格式可以是zip/tar.gz/exe形式的。如下图所示,在64位windows中使用tomcat,我们可以下载"64-bit Windows.zip",直接解…

33迭代器模式(Iterator Pattern)

动机(Motivate): 在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“ 同一种算法在多种集合对象上进行操作…

32命令模式(Command Pattern)

耦合与变化: 耦合是软件不能抵御变化灾难的根本性原因。不仅实体对象与实体对象之间存在耦合关系,实体对象与行为操作之间也存在耦合关系。 动机(Mot…

34观察者模式(Observer Pattern)

动机(Motivate):在软件构建 过程中,我们需要为某些对象建立一种“通知依赖关系” --------一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密&#x…

36中介者模式(Mediator Pattern)

依赖关系的转化: 动机(Motivate): 在软件构建过程中,经常会出现多个对象互相关联交互的情况,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。 在这种情况…

相似理论与模型试验_正交实验下的固液耦合相似材料研究

原标题:基于正交试验的固液耦合相似材料研究摘 要:为了研究矿井突水演化规律,通过正交试验研制出一种能同时满足固体力学与水理性的固液 耦合相似材料,该相似材料以河沙为骨料、水泥和大白粉为胶结剂、液体石蜡和淀粉为调节剂。采用 极差分析…

35解释器模式(Interpreter Pattern)

动机(Motivate): 在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。 在这种情况下,将特定领域的问题表达为某种文法规则下的句子,…

37职责链模式(Chain of Responsibility Pattern)

动机(Motivate): 在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合。 如何使请求的发送者不需要指定具体的接受…

python3中format函数列表_Python3之字符串格式化format函数详解(上)

173.jpg概述在Python3中,字符串格式化操作通过format()方法或者fstring实现。而相比于老版的字符串格式化方式,format()方法拥有更多的功能,操作起来更加方便,可读性也更强。该函数将字符串当成一个模板,通过传入的参数…

38备忘录模式(Memento Pattern)

对象状态的回溯: 对象状态的变化无端,如何回溯/恢复对象在某个点的状态? 动机: 在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够…

39策略模式(Strategy Pattern)

算法与对象的耦合: 对象可能经常需要使用多种不同的算法,但是如果变化频繁,会将类型变得脆弱... 动机: 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将…

40访问者模式(Visitor Pattern)

类层次结构的变化: 类层次结构中可能经常由于引入新的操作,从而将类型变得脆弱... 动机: 在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接…

41状态模式(State Pattern)

对象状态影响对象行为: 对象拥有不同的状态,往往会行使不同的行为... 动机: 在软件构建过程中,某些对象的状态如果改变以及其行为也会随之而发生变化,比如文档处于只读状态,其支…

python中空格属于字符吗_举例说明python中空格是属于字符

python中空格属于字符吗?答案是肯定的,空格在Python中也是属于字符的。案例:输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。#!/usr/bin/python# -*- coding: UTF-8 -*-import strings raw_input(input a st…

【转】如何将域中的AD数据导入SharePoint

最近刚装好sharepoint2010,想要研究一下,第一件想做的事就是想把AD中的用户信息导入到SharePoint中。 那现在就来看看我是怎么操作的: 1.打开管理中心 sharepoint是通过“用户配置文件同步服务”来实现同步,所以第一步要开启这个…

Apsara Clouder专项技能认证:实现调用API接口

一.API 简介 1.API 的概念 API(Application Programming Interface应用程序编程接口)是一些预定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码或理解内部工作机制的细节 2.API 的特点 API 是一个明确定义的接口,可以为其…

第一节:复习委托,并且通过委托的异步调用开启一个新线程和异步回调、异步等待

一. 再谈委托 1. 委托是一个关键字为delegate的自定义类型,通过委托可以把方法以参数的形式传递给另外一个方法,实现插件式的开发模式; 同时调用委托的时候,委托所包含的所有方法都会被实现。 2. 委托的发展历史:new…

linux 修改 java 内存_Linux 和 Windows修改Java虚拟机内存大小

因为内存溢出问题1. Linux下直接修改%tomcat_home%/bin/catalina.sh文件在注释下紧接一行也就是脚本正文开始之前 加上Java_OPTS-server -Xms512m -Xmx1024m -XX:PermSize128m -XX:MaxPermSize512m如果报-x没有定义,则用declare -x JAVA_OPTS"-Xms512m -Xmx1024"初始…

第二节:深入剖析Thread的五大方法、数据槽、内存栅栏

一. Thread及其五大方法 Thread是.Net最早的多线程处理方式,它出现在.Net1.0时代,虽然现在已逐渐被微软所抛弃,微软强烈推荐使用Task(后面章节介绍),但从多线程完整性的角度上来说,我们有必要了解下N年前多线程的是怎么…

java redis 生成唯一id_Redis在集群环境中生成唯一ID

概述设计目标:每秒最大生成10万个ID,ID单调递增且唯一。Reidis可以不需要持久化ID。要求:集群时钟不能倒退。总体思路:集群中每个节点预生成生成ID;然后与redis的已经存在的ID做比较。如果大于,则取节点生成的ID&#…