Thursday, January 3, 2008

Template Method Design Pattern

Consider a business application where your application receives data in different representations – XML, Text file, SQL data table etc. You need to print this data in a tabular format with a common header, footer, page size settings among other things. We can quickly cook up a couple of designs to accomplish this. At the simplest level, we can create a single module that prints the header, then based on the appropriate type of data, extracts the information from the data representations and prints individual rows of data, then prints the footer.

PrintFormatter Class Listing

Public Class PrintFormatter

 

  Public Enum DataRepresentation

    TextFormat

    XMLFormat

    SQLDataTableFormat

  End Enum

 

  Private _TextData As String

  Private _XMLData As Xml.XmlDocument

  Private _SQLData As DataTable

  Private _DataRepresentation As DataRepresentation

 

  Public Sub New(ByVal PrintDataRepresentation As DataRepresentation)

    _DataRepresentation = PrintDataRepresentation

  End Sub

 

  Public WriteOnly Property TextData() As String

    Set(ByVal value As String)

      _TextData = value

    End Set

  End Property

 

  Public WriteOnly Property XMLData() As Xml.XmlDocument

    Set(ByVal value As Xml.XmlDocument)

      _XMLData = value

    End Set

  End Property

 

  Public WriteOnly Property SQLData() As DataTable

    Set(ByVal value As DataTable)

      _SQLData = value

    End Set

  End Property

 

  Public Sub PrintFormattedData()

    _PrintHeader()

    Select Case _DataRepresentation

      Case DataRepresentation.TextFormat

        _PrintTextData()

      Case DataRepresentation.XMLFormat

        _PrintXMLData()

      Case DataRepresentation.SQLDataTableFormat

        _PrintSQLData()

    End Select

    _PrintFooter()

  End Sub

 

  Private Sub _PrintHeader()

    Console.WriteLine("This represents the page header")

  End Sub

 

  Private Sub _PrintFooter()

    Console.WriteLine("This represents the page footer")

  End Sub

 

  Private Sub _PrintTextData()

    Console.WriteLine("This represents the formatted text data - " & _TextData)

  End Sub

 

  Private Sub _PrintXMLData()

    Console.WriteLine("This represents the formatted XML data - " & _XMLData.OuterXml)

  End Sub

 

  Private Sub _PrintSQLData()

    Console.WriteLine("This represents the formatted SQL data - " & _SQLData.TableName)

  End Sub

 

End Class



Not much of a class diagram hierarchy to show



modMain Module Listing

Module modMain

 

  Sub Main()

 

    'Sample Data - one of each kind

    Dim TextData As String = "<<TextData>>"

    Dim XMLData As Xml.XmlDocument = New Xml.XmlDocument

    XMLData.LoadXml("<xMLData />")

    Dim SQLData As DataTable = New DataTable("SQLDataTable")

 

    Dim oPrintFormatter As PrintFormatter

    oPrintFormatter = New PrintFormatter(PrintFormatter.DataRepresentation.TextFormat)

    oPrintFormatter.TextData = TextData

    oPrintFormatter.PrintFormattedData()

 

    oPrintFormatter = New PrintFormatter(PrintFormatter.DataRepresentation.XMLFormat)

    oPrintFormatter.XMLData = XMLData

    oPrintFormatter.PrintFormattedData()

 

    oPrintFormatter = New PrintFormatter(PrintFormatter.DataRepresentation.SQLDataTableFormat)

    oPrintFormatter.SQLData = SQLData

    oPrintFormatter.PrintFormattedData()

 

    Console.ReadLine()

  End Sub

 

End Module



Let’s put our application to the test



Alright, things look good for a one off application requirement. However, extending this design to support additional data formats would require modifying the PrintFormatter class by adding additional data representations and modifying the PrintFormattedData method. A template method pattern can be effectively used in this example to eliminate the need to modify the base class and extending the base class to support additional data formats.
The primary idea behind the template method is to use a method to perform the actions that are common across representations – printing the header and footer while leaving the task of printing specific data representations to abstract methods. The PrintFormatter class can then be extended by the TextPrintFormatter to provide the implementation for printing text data. Similarly the XMLPrintFormatter can implement the abstract PrintSpecificData method to print XML Data while the SQLPrintFormatter can provide the SQL data printing logic.

PrintFormatter Class Listing

Public MustInherit Class PrintFormatter

 

  'Our Template method

  Public Sub PrintFormattedData()

    PrintHeader()

    PrintSpecificData()

    PrintFooter()

  End Sub

 

  'Some methods that provide a default implementation

  Public Sub PrintHeader()

    Console.WriteLine("This represents the page header")

  End Sub

 

  Public Sub PrintFooter()

    Console.WriteLine("This represents the page footer")

  End Sub

 

  'Abstract methods

  Public MustOverride Sub PrintSpecificData()

 

End Class



TextPrintFormatter Class Listing

Public Class TextPrintFormatter

  Inherits PrintFormatter

 

  Private _TextData As String

 

  Public Sub New(ByVal TextData As String)

    _TextData = TextData

  End Sub

 

  Public Overrides Sub PrintSpecificData()

    Console.WriteLine("This represents the formatted text data - " & _TextData)

  End Sub

End Class



XMLPrintFormatter Class Listing

Public Class XMLPrintFormatter

  Inherits PrintFormatter

 

  Private _XMLData As Xml.XmlDocument

 

  Public Sub New(ByVal XMLData As Xml.XmlDocument)

    _XMLData = XMLData

  End Sub

 

  Public Overrides Sub PrintSpecificData()

    Console.WriteLine("This represents the formatted XML data - " & _XMLData.OuterXml)

  End Sub

End Class



SQLDataPrintFormatter Class Listing

Public Class SQLDataPrintFormatter

  Inherits PrintFormatter

 

  Private _SQLData As DataTable

 

  Public Sub New(ByVal SQLData As DataTable)

    _SQLData = SQLData

  End Sub

 

  Public Overrides Sub PrintSpecificData()

    Console.WriteLine("This represents the formatted SQL data - " & _SQLData.TableName)

  End Sub

End Class



The Template method pattern class hierarchy



modMain Module Listing

Module modMain

 

  Sub Main()

    'Sample Data - one of each kind

    Dim TextData As String = "<<TextData>>"

    Dim XMLData As Xml.XmlDocument = New Xml.XmlDocument

    XMLData.LoadXml("<xMLData />")

    Dim SQLData As DataTable = New DataTable("SQLDataTable")

 

    Dim oPrintFormatter As PrintFormatter

 

    oPrintFormatter = New TextPrintFormatter(TextData)

    oPrintFormatter.PrintFormattedData()

 

    oPrintFormatter = New XMLPrintFormatter(XMLData)

    oPrintFormatter.PrintFormattedData()

 

    oPrintFormatter = New SQLDataPrintFormatter(SQLData)

    oPrintFormatter.PrintFormattedData()

 

    Console.ReadLine()

  End Sub

 

End Module



And a look at the results

No comments: