Chapter 31. The Template Method Pattern

The Template Method pattern is a very simple pattern that you will find yourself using frequently. Whenever you write a parent class where you leave one or more of the methods to be implemented by derived classes, you are in essence using the Template pattern. The Template pattern formalizes the idea of defining an algorithm in a class but leaving some of the details to be implemented in subclasses. In other words, if your base class is an abstract class, as often happens in these design patterns, you are using a simple form of the Template pattern.

Since inheritance is a critical part of this pattern, we will develop our Template Method example exclusively in VB7.

Motivation

Templates are so fundamental, you have probably used them dozens of times without even thinking about it. The idea behind the Template pattern is that some parts of an algorithm are well defined and can be implemented in the base class, whereas other parts may have several implementations and are best left to derived classes. Another main theme is recognizing that there are some basic parts of a class that can be factored out and put in a base class so they do not need to be repeated in several subclasses.

For example, in developing the BarPlot and LinePlot classes we used in the Strategy pattern examples in the previous chapter, we discovered that in plotting both line graphs and bar charts we needed similar code to scale the data and compute the x and y pixel positions.

Public MustInherit Class PlotWindow
    Inherits System.Windows.Forms.Form
    'base plot window class for bar and line plots
    Protected xfactor As Single, xpmin As Single
    Protected xpmax As Single
    Protected xmin As Single, xmax As Single
    Protected ymin As Single, ymax As Single
    Protected yfactor As Single, ypmin As Single
    Protected ypmax As Single
    Protected x() As Single, y() As Single
    Protected bPen As Pen
    Protected hasData As Boolean
    Protected w As Integer, h As Integer
    Const max As Single = 1E+38

    '-----
    Public Sub setPenColor(ByVal c As Color)
        bpen = New Pen(c)
    End Sub
    '-----
    Private Sub findBounds()
        'on CD-Rom
      End Sub
    '-----
    Public Overridable Sub set_Bounds( _
            ByVal pic As PictureBox)
        findBounds()
        'on CD-Rom
    End Sub
    '-----
    Public Function calcx(ByVal xp As Single) As Integer
        Dim ix As Integer
        ix = ((xp - xmin) * xfactor + xpmin).ToInt16
        Return ix
    End Function
    '-----
    Public Function calcy(ByVal yp As Single) As Integer
        Dim iy As Integer
        yp = ((yp - ymin) * yfactor).ToInt16
        iy = (ypmax - yp).ToInt16
        Return iy
    End Function
    '-----
    Public Sub plot(ByVal xp() As Single, _
                    ByVal yp() As Single, ByVal pic As PictureBox)
        x = xp
        y = yp
        set_Bounds(pic)    'compute scaling factors
        hasData = True
        repaint()
    End Sub
    '-----
    Public MustOverride Sub repaint()
    '-----
End Class

Thus, these methods all belonged in a base PlotPanel class without any actual plotting capabilities. Note that the plot method sets up all the scaling constants and just calls repaint. The actual repaint method is deferred to the derived classes. It is exactly this sort of extension to derived classes that exemplifies the Template Method pattern.

Kinds of Methods in a Template Class

As discussed in Design Patterns, the Template Method pattern has four kinds of methods that you can use in derived classes.

  1. Complete methods that carry out some basic function that all the subclasses will want to use, such as calcx and calcy in the preceding example. These are called Concrete methods.

  2. Methods that are not filled in at all and must be implemented in derived classes. In VB7, you would declare these as MustOverride methods.

  3. Methods that contain a default implementation of some operations but that may be overridden in derived classes. These are called Hook methods. Of course, this is somewhat arbitrary because in VB7 you can override any public or protected method in the derived class but Hook methods, however, are intended to be overridden, whereas Concrete methods are not.

  4. Finally, a Template class may contain methods that themselves call any combination of abstract, hook, and concrete methods. These methods are not intended to be overridden but describe an algorithm without actually implementing its details. Design Patterns refers to these as Template methods.

Sample Code

Let's consider a simple program for drawing triangles on a screen. We'll start with an abstract Triangle class and then derive some special triangle types from it, as we see in Figure 31-1.

The abstract Triangle class and three of its subclasses

Figure 31-1. The abstract Triangle class and three of its subclasses

Our abstract Triangle class illustrates the Template pattern.

Public MustInherit Class Triangle
    Private p1, p2, p3 As Point
    Protected bPen As Pen
    '-----
    Public Sub New(ByVal a As Point, ByVal b As Point, _
             ByVal c As Point)
        p1 = a
        p2 = b
        p3 = c
        bPen = New Pen(Color.Black)
    End Sub
    '-----
    'draw the complete triangle
    Public Sub draw(ByVal g As Graphics)
        drawLine(g, p1, p2)
        Dim c As Point = draw2ndLine(g, p2, p3)
        closeTriangle(g, c)
    End Sub
    '-----
    'draw one line
    Public Sub drawLine(ByVal g As Graphics, _
        ByVal a As Point, ByRef b As Point)
        g.drawLine(bpen, a.x, a.y, b.x, b.y)
    End Sub
    '-----
    'method you must override in derived classes
    Public MustOverride Function draw2ndLine(_
          ByVal g As Graphics, _
        ByVal a As Point, ByVal b As Point) As Point
    '-----
    'close by drawing back to beginning
    Public Sub closeTriangle(ByVal g As Graphics, _
          ByVal c As Point)
        g.DrawLine(bpen, c.X, c.Y, p1.x, p1.y)
    End Sub
End Class

This Triangle class saves the coordinates of three lines, but the draw routine draws only the first and the last lines. The all-important draw2ndLine method that draws a line to the third point is left as an abstract method. That way the derived class can move the third point to create the kind of rectangle you wish to draw.

This is a general example of a class using the Template pattern. The draw method calls two concrete base class methods and one abstract method that must be overridden in any concrete class derived from Triangle.

Another very similar way to implement the case triangle class is to include default code for the draw2ndLine method.

Public Overridable Function draw2ndLine(_
        ByVal g As Graphics, ByVal a As point, _
        ByVal b As Point) As Point
        g.DrawLine(bpen, a.X, a.Y, b.X, b.Y)
        Return b
    End Function

In this case, the draw2ndLine method becomes a Hook method that can be overridden for other classes.

Drawing a Standard Triangle

To draw a general triangle with no restrictions on its shape, we simple implement the draw2ndLine method in a derived stdTriangle class.

Public Class StdTriangle
    Inherits Triangle
    '-----
    Public Sub new(ByVal a As Point, _
            ByVal b As Point, ByVal c As Point)
        MyBase.new(a, b, c)
    End Sub
    '-----
    Public Overrides Function draw2ndLine(_
        ByVal g As Graphics, ByVal a As point, _
        ByVal b As Point) As Point
        g.DrawLine(bpen, a.X, a.Y, b.X, b.Y)
        Return b
    End Function
End Class

Drawing an Isosceles Triangle

This class computes a new third data point that will make the two sides equal in length and saves that new point inside the class.

Public Class IsoscelesTriangle
    Inherits Triangle
    Private newc As Point
    Private newcx, newcy As Integer
    '-----
    Public Sub New(ByVal a As Point, ByVal b As Point, _
                   ByVal c As Point)
        MyBase.New(a, b, c)
        Dim dx1, dy1, dx2, dy2, side1, side2 As Single
        Dim slope, intercept As Single
        Dim incr As Integer
        dx1 = b.x - a.x
        dy1 = b.y - a.y
        dx2 = c.x - b.x
        dy2 = c.y - b.y

        side1 = calcSide(dx1, dy1)
        side2 = calcSide(dx2, dy2)

        If (side2 < side1) Then
            incr = -1
        Else
            incr = 1
        End If
        slope = dy2 / dx2
        intercept = c.y - slope * c.X

        'move point c so that this is an isosceles triangle
        newcx = c.X
        newcy = c.Y
        While (abs(side1 - side2) > 1)
            newcx = newcx + incr
            'iterate a pixel at a time until close
            newcy = (slope * newcx + intercept).ToInt16
            dx2 = newcx - b.x
            dy2 = newcy - b.y
            side2 = calcSide(dx2, dy2)
        End While
        newc = New Point(newcx, newcy)
    End Sub
    '-----
    Private Function calcSide(ByVal dx As Single, _
            ByVal dy As Single) As Single
        Return Sqrt(dx * dx + dy * dy).ToSingle
    End Function

When the Triangle class calls the draw method, it calls this new version of draw2ndLine and draws a line to the new third point. Further, it returns that new point to the draw method so it will draw the closing side of the triangle correctly.

'draw 2nd line using new saved point
    Public Overrides Function draw2ndLine( _
        ByVal g As Graphics, ByVal b As Point, _
            ByVal c As Point) As Point
        g.DrawLine(bpen, b.X, b.Y, newc.X, newc.Y)
        Return newc
    End Function

The Triangle Drawing Program

The main program simply creates instances of the triangles you want to draw. Then it adds them to an ArrayList in the TriangleForm class.

Public Class Form1
    Inherits System.Windows.Forms.Form
    Private triangles As ArrayList
    Private Sub init()
        'Create a list of triangles to draw
        triangles = New ArrayList()
        Dim t1 As New StdTriangle( _
            New Point(10, 10), New Point(150, 50), _
            New Point(100, 75))
        Dim t2 As New IsocelesTriangle( _
            New Point(150, 100), New Point(240, 40), _
            New Point(175, 150))
        triangles.Add(t1)
        triangles.Add(t2)
    End Sub

It is the paint routine in this class that actually draws the triangles.

Public Sub Pic_Paint(ByVal sender As Object, _
        ByVal e As System.WinForms.PaintEventArgs) _
            Handles Pic.Paint
        Dim i As Integer
        Dim g As Graphics = e.Graphics
        For i = 0 To triangles.Count - 1
            Dim t As Triangle = _
                  CType(triangles(i), triangle)
            t.draw(g)
        Next
    End Sub

A standard triangle and an isosceles triangle are shown in Figure 31-2.

A standard triangle and an isosceles triangle

Figure 31-2. A standard triangle and an isosceles triangle

Templates and Callbacks

Design Patterns points out that Templates can exemplify the “Hollywood Principle,” or “Don't call us, we'll call you.” The idea here is that methods in the base class seem to call methods in the derived classes. The operative word here is seem. If we consider the draw code in our base Triangle class, we see that there are three method calls.

drawLine(g, p1, p2)
Dim c As Point = draw2ndLine(g, p2, p3)
closeTriangle(g, c)

Now drawLine and closeTriangle are implemented in the base class. However, as we have seen, the draw2ndLine method is not implemented at all in the base class, and various derived classes can implement it differently. Since the actual methods that are being called are in the derived classes, it appears as though they are being called from the base class.

If this idea makes you uncomfortable, you will probably take solace in recognizing that all the method calls originate from the derived class and that these calls move up the inheritance chain until they find the first class that implements them. If this class is the base class—fine. If not, it could be any other class in between. Now, when you call the draw method, the derived class moves up the inheritance tree until it finds an implementation of draw. Likewise, for each method called from within draw, the derived class starts at the current class and moves up the tree to find each method. When it gets to the draw2ndLine method, it finds it immediately in the current class. So it isn't “really” called from the base class, but it does seem that way.

Summary and Consequences

Template patterns occur all the time in OO software and are neither complex nor obscure in intent. They are a normal part of OO programming, and you shouldn't try to make them into more than they actually are.

The first significant point is that your base class may only define some of the methods it will be using, leaving the rest to be implemented in the derived classes. The second major point is that there may be methods in the base class that call a sequence of methods, some implemented in the base class and some implemented in the derived class. This Template method defines a general algorithm, although the details may not be worked out completely in the base class.

Template classes will frequently have some abstract methods that you must override in the derived classes, and they may also have some classes with a simple “placeholder” implementation that you are free to override where this is appropriate. If these placeholder classes are called from another method in the base class, then we call these overridable methods “Hook” methods.

Programs on the CD-ROM

StrategyTemplateStrategy VB7 plot strategy using Template method pattern
TemplateTngle VB7 triangle drawing template
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset