模板设计模式的六种常见用法:设计模式系列






4.95/5 (23投票s)
在本文中,我们将了解模板设计模式的六种常见用法。
目录
- 引言和目标
- 模板模式简介
- 场景 1:灵活可扩展的通用特化用户界面
- 场景 2:ASP.NET 页面生命周期
- 场景 3:代码生成器
- 场景 4:XML 解析器
- 场景 5:业务组件中的验证
- 场景 6:可定制的日志记录实用工具
- 设计模式的实时可视化图
- 设计模式参考
引言和目标
在本文中,我们将尝试理解模板设计模式的六个重要用途。模板设计模式是许多地方被有意识或无意识使用的模式之一。本文将详细介绍该模式显而易见的六个应用场景。
定义:模板模式用于我们希望在泛化和特化关系中创建可扩展行为的场景。
如果您是设计模式的新手,可以先观看我的 工厂模式视频。这是一个很老的视频了,抱歉质量不高,我刚开始录制的时候还很新手。
您也可以查看末尾的参考部分,那里有我关于设计模式的完整四部分系列。
模板模式简介
模板模式属于行为模式类别。模板模式定义了一个主流程模板,该主流程模板按顺序调用子流程。之后,可以修改主流程的子流程以生成不同的行为。
例如,下面是一个将数据解析并加载到 Oracle 的简单过程。整个过程有三个固定步骤:
- 从源加载数据
- 解析数据
- 将数据转储到 Oracle
现在,您可以修改“加载”和“解析”的实现来创建一个 CSV 文件加载过程。调用“加载”、“解析”和“转储”的整体顺序将保持不变,但我们可以完全自由地更改“加载”、“解析”和“转储”的实现,从而创建新过程。
从上图可以看出,我们修改了“加载”和“解析”子流程来生成 CSV 文件和 SQL Server 加载过程。“转储”函数以及调用子流程的顺序在子流程中未改变。
为了实现模板模式,我们需要遵循四个重要步骤:
- 通过创建父抽象类来创建模板或主进程。
- 通过定义抽象方法和函数来创建子进程。
- 创建一个定义子进程调用顺序的方法。此方法应定义为普通方法,以便子方法无法覆盖它。
- 最后创建子类,这些子类可以修改抽象方法或子进程以定义新实现。
public abstract class GeneralParser
{
protected abstract void Load();
protected abstract void Parse();
protected virtual void Dump()
{
Console.WriteLine("Dump data in to oracle");
}
public void Process()
{
Load();
Parse();
Dump();
}
}
SqlServerParser
继承自 GeneralParser
,并用 SQL Server 实现覆盖了 Load
和 Parse
方法。
public class SqlServerParser : GeneralParser
{
protected override void Load()
{
Console.WriteLine("Connect to SQL Server");
}
protected override void Parse()
{
Console.WriteLine("Loop through the dataset");
}
}
FileParser
继承自 GeneralParser
,并用文件特定实现覆盖了 Load
和 Parse
方法。
public class FileParser : GeneralParser
{
protected override void Load()
{
Console.WriteLine("Load the data from the file");
}
protected override void Parse()
{
Console.WriteLine("Parse the file data");
}
}
现在,客户端可以调用这两个解析器。
FileParser ObjFileParser = new FileParser();
ObjFileParser.Process();
Console.WriteLine("-----------------------");
SqlServerParser ObjSqlParser = new SqlServerParser();
ObjSqlParser.Process();
Console.Read();
下面显示了这两个解析器的输出。
Load the data from the file
Parse the file data
Dump data in to oracle
-----------------------
Connect to SQL Server
Loop through the dataset
Dump data in to oracle
现在,让我们看看“模板”模式可以实现的几个实际场景。
场景 1:灵活可扩展的通用特化用户界面
很多时候我们会遇到看起来几乎相同但外观和感觉略有不同的 UI。例如,下图中的屏幕数据相同,但背景颜色不同。
在这种情况下,窗体构造函数将成为主进程,它将调用三个进程/函数:
InitializeComponent
:这将创建窗体所需的 UI 对象。LoadCustomer
:此函数将加载数据并绑定到网格。LoadGrid
:此函数将定义网格的外观和感觉。
public Form1()
{
InitializeComponent();
LoadCustomer();
LoadGrid();
}
下面是基窗体的完整代码。请注意:LoadGrid
是一个抽象方法。换句话说,我们可以创建具有不同颜色实现的窗体,而不会干扰窗体的其余部分。
public abstract partial class Form1 : Form
{
protected Customers objCustomers = new Customers();
protected List<customer> oCustomerList;
public Form1()
{
InitializeComponent();
LoadCustomer();
LoadGrid();
}
public void LoadCustomer()
{
oCustomerList = objCustomers.GetCustomers();
}
public abstract void LoadGrid();
}
如果您想创建一个具有不同背景的新窗体,可以保持其他代码不变,只需用不同的颜色/外观覆盖 LoadGrid
功能即可。下面是示例代码:
public partial class Form3 : Form1
{
....
....
....
....
public override void LoadGrid()
{
dgGridCustomer.DataSource = oCustomerList;
dgGridCustomer.BackgroundColor = Color.Aqua;
dgGridCustomer.ForeColor = Color.Brown;
}
}
场景 2:ASP.NET 页面生命周期
模板模式非常明显的场景之一是 ASP.NET 页面生命周期。在 ASP.NET 页面生命周期中,生命周期顺序是固定的,但它提供了完全的权限来覆盖每个序列的实现。
例如,在 ASP.NET 页面生命周期中,我们有 Init、Load、Validate、Prerender、Render 等各种事件。顺序是固定的,但我们可以根据需要覆盖每个事件的实现。
场景 3:代码生成器
我们经常使用 T4 模板、LINQ、EF 等代码生成器从数据库表设计中生成代码。现在,代码生成器在单独的物理文件中工作,它会为您生成代码。因此,如果更改表设计,它将重新生成文件。
现在,如果您想添加一些自定义代码,则无法更改自动生成的代码文件,因为当 DB 设计更改时,您的代码将被替换。因此,最好的方法是使用单独的文件扩展生成的代码类,并将您的自定义代码放在该类中。使用模板模式可以非常有效地完成此扩展。生成的代码类可以定义一个固定流程,但同时提供空的虚拟方法、属性和函数,这些可以扩展以注入到您的自定义逻辑中。
场景 4:XML 解析器
适用于模板模式的另一个场景是 XML 解析器。在 XML 中,我们通常解析父元素和子元素。在许多场景中,解析几乎是通用的,只有子元素有一些小的变化。
例如,在下面的代码片段中,我们有 Customer
作为父元素,每个客户将有 Orders
,Orders
将有 Product
。现在,Customer
和 Orders
的解析将是相同的,但 Product
标签可以根据情况有一个 Size
属性。例如,在下面的代码片段中,Product
元素只有产品的名称和数量。
<Customer Name="Shiv">
<Orders OrderNumber="1001">
<Product Name="Shirts" Amount="1000"/>
<Product Name="Socks" Amount="100"/>
</Orders>
</Customer>
在某些情况下,您的 Product
元素可能有其他变体,如下面的 XML 代码片段所示。在这种情况下,您可以只覆盖 Product
元素的解析过程,并保持整体 XML 解析过程的顺序不变。
<Customer Name="Shiv">
<Orders OrderNumber="1001">
<Product Name="Shirts" Amount="1000">
<LargeSize/>
</Product>
<Product Name="Socks" Amount="1000">
<SmallSize/>
</Product>
</Orders>
</Customer>
场景 5:业务组件中的验证
业务类具有验证功能,我们希望创建具有不同验证逻辑的业务类版本。
public class Supplier
{
private string _SupplierCode;
public string SupplierCode
{
get
{
return _SupplierCode;
}
set
{
ValidateSuppCode(value);
_SupplierCode = value;
}
}
public virtual void ValidateSuppCode(string SuppCode)
{
if (SuppCode.Length == 0)
{
throw new Exception("Can not be null");
}
}
}
public class SupplierNew : Supplier
{
public override void ValidateSuppCode(string SuppCode)
{
base.ValidateSuppCode(SuppCode);
if (SuppCode.Length > 10)
{
throw new Exception("can not be more than 10");
}
}
}
场景 6:可定制的日志记录实用工具
这是模板模式非常适合的另一个场景。如果您查看这些组件,即消息记录器、错误记录器等,它们会分两个阶段执行:第一阶段准备消息,第二阶段记录消息。
对于这些类型的场景,我们可以创建一个父类,该类定义两个固定顺序:一个准备消息,另一个将消息记录到源(文件、事件查看器、电子邮件等)。
之后,我们可以创建子类,它们可以继承并覆盖这些阶段的逻辑,但保持顺序不变。
设计模式的实时可视化图
设计模式参考
- 第一部分 设计模式 FAQ -- 工厂模式、抽象工厂模式、建造者模式、原型模式、单例模式和命令模式
- 第二部分 设计模式 FAQ -- 解释器模式、迭代器模式、中介者模式、备忘录模式和观察者模式
- 第三部分 设计模式 FAQ -- 状态模式、策略模式、访问者模式、适配器模式和享元模式
- 第四部分 设计模式 FAQ -- 桥模式、组合模式、装饰器模式、外观模式、责任链模式 (COR)、代理模式和模板模式
欢迎免费下载这些 FAQ PDF,以及我网站上的设计模式文章,我收集了约 400 个关于 SilverLight、Azure、VSTS、WCF、WPF、WWF、SharePoint、设计模式、UML 等的 FAQ 问题和答案。
进一步阅读,请观看下面的面试准备视频和分步视频系列。