★★★★★
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
结构图
角色
- 抽象模板(Subject)角色:定义一个或队歌抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。同时定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
- 代理主题(Proxy)角色:实现父类定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现。从而使得顶级逻辑的实现各不相同。
动机
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
意图
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
示意性代码
示意性代码
'MainApp test application
Public Class MainApp
Public Shared Sub Main()
Dim c As AbstractClass
c = New ConcreteClassA
c.TemplateMethod()
c = New ConcreteClassB
c.TemplateMethod()
'Wait for user
Console.ReadLine()
End Sub
End Class
'"AbstractClass"
Public MustInherit Class AbstractClass
'The "Template method"
Public Sub TemplateMethod()
Call PrimitiveMethod1()
Call PrimitiveMethod2()
Console.WriteLine()
End Sub
Protected MustOverride Sub PrimitiveMethod1()
Protected MustOverride Sub PrimitiveMethod2()
End Class
'"ConcreteClass"
Public Class ConcreteClassA
Inherits AbstractClass
Protected Overrides Sub PrimitiveMethod1()
Console.WriteLine("ConcreteClassA.PrimitiveMethod1")
End Sub
Protected Overrides Sub PrimitiveMethod2()
Console.WriteLine("ConcreteClassA.PrimitiveMethod2")
End Sub
End Class
'"ConcreteClass"
Public Class ConcreteClassB
Inherits AbstractClass
Protected Overrides Sub PrimitiveMethod1()
Console.WriteLine("ConcreteClassB.PrimitiveMethod1")
End Sub
Protected Overrides Sub PrimitiveMethod2()
Console.WriteLine("ConcreteClassB.PrimitiveMethod2")
End Sub
End Class
一个实例
下面的模板方法代码演示了读取nwind.mdb数据库的数据并将其显示出来。
实例代码
'MainApp test application
Public Class MainApp
Public Shared Sub Main()
Dim dao As DataAccessObject
dao = New Products
dao.Run()
dao = New Categories
dao.Run()
'Wait for user
Console.ReadLine()
End Sub
End Class
'"AbstractClass"
Public MustInherit Class DataAccessObject
Protected connection As OleDbConnection
Protected dataset As DataSet
Protected Overridable Sub Connect()
'Make sure mdb is on c:\
Dim connectstring As String
connectstring = "Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source=C:\nwind.mdb;"
If connection Is Nothing Then
connection = New OleDbConnection(connectstring)
End If
If connection.State = ConnectionState.Closed Then
connection.Open()
End If
End Sub
Protected MustOverride Sub [Select]()
Protected MustOverride Sub Process()
Protected Sub Disconnect()
If connection IsNot Nothing _
And connection.State = ConnectionState.Open Then
connection.Close()
End If
End Sub
'The "Template Method"
Public Sub Run()
Call Connect()
Call [Select]()
Call Process()
Call Disconnect()
Console.WriteLine()
End Sub
End Class
'"ConcreteClass"
Public Class Products
Inherits DataAccessObject
Protected Overrides Sub [Select]()
Dim aSql As String
aSql = "Select ProductName From ProductTable"
Dim dataAdapter As New OleDbDataAdapter(aSql, connection)
dataset = New DataSet()
dataAdapter.Fill(dataset, "ProductTable")
End Sub
Protected Overrides Sub Process()
Console.WriteLine("Products ---- ")
Dim dataTable As DataTable = dataset.Tables("ProductTable")
For Each row As DataRow In dataTable.Rows
Console.WriteLine(row("ProductName"))
Next
End Sub
End Class
'"ConcreteClass"
Public Class Categories
Inherits DataAccessObject
Protected Overrides Sub [Select]()
Dim aSql As String
aSql = "Select CategoryName From CategoryTable"
Dim dataAdapter As New OleDbDataAdapter(aSql, connection)
dataset = New DataSet()
dataAdapter.Fill(dataset, "CategoryTable")
End Sub
Protected Overrides Sub Process()
Console.WriteLine("Categories ---- ")
Dim dataTable As DataTable = dataset.Tables("CategoryTable")
For Each row As DataRow In dataTable.Rows
Console.WriteLine(row("CategoryName"))
Next
End Sub
End Class
Template Method模式的几个要点:
1、Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
2、除了可以灵活应对子步骤的变化外,“不用调用我,让我来调用你”(Don't call me,let me call you)的反向控制结构是Template Method的典型应用。
3、在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法,纯虚方法),但一般推荐将它们设置为protected方法。
我的理解
封装算法结构,支持算法子步骤变化。
参考资料
《C#面向对象设计模式纵横谈系列课程(14)》 李建中老师