Chapter 25. The Template Pattern

In this chapter, we take up the Template pattern—a very simple pattern that you will discover you use all the time. Whenever you write a parent class and leave one or more of the methods to be implemented by derived classes, you are in essence using the Template pattern. This pattern formalizes the idea of defining an algorithm in a class, while 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. We note that Design Patterns refers to this as the Template Method Pattern, but we shorten the name in this chapter for convenience.

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 the algorithm are well defined and can be implemented in the base class, while other parts may have several implementations and are best left to derived classes. Another main idea is recognizing that there are some basic parts of a class that can be factored out and put in a base class so that they do not need to be repeated in several subclasses.

For example, in developing the PlotPanel classes used in the Strategy pattern examples, 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 class PlotPanel extends JPanel {
    private float xfactor, yfactor;
    private int   xpmin, ypmin, xpmax, ypmax;
    private float minX, maxX, minY, maxY;
    private float x[], y[];
    private Color color;
//--------------
    public void setBounds(float minx, float miny,
            float maxx, float maxy) {
        minX = minx;
        maxX = maxx;
        minY = miny;
        maxY = maxy;
    }
//--------------
    public void plot(float[] xp, float[] yp, Color c) {
        x = xp;        //copy in the arrays
        y = yp;
        color = c;    //and color
        //compute the bounds and scaling factors
        int w = getWidth() - getInsets().left - getInsets().right;
        int h = getHeight() - getInsets().top -
            getInsets().bottom;

        xfactor = (0.9f * w) /(maxX - minX);
        yfactor = (0.9f * h)/ (maxY - minY);

        xpmin = (int)(0.05f * w);
        ypmin = (int)(0.05f * h);
        xpmax = w - xpmin;
        ypmax = h - ypmin;
        repaint();      //this causes the actual plot
    }
//--------------
    protected int calcx(float xp) {
        return(int)((xp-minX) * xfactor + xpmin);
    }
//--------------
    protected int calcy(float yp) {
        int ypnt = (int)((yp-minY) * yfactor);
        return ypmax - ypnt;
    }
}

Thus these methods all belong in a base PlotPanel class without any actual plotting capabilities. Note that the plot method sets up all of the scaling constants and then calls repaint. The actual paint method is deferred to the derived classes. Since the JPanel class always has a paint method, we don't want to declare it as an abstract method in the base class, but we do need to override it in the derived classes.

Kinds of Methods in a Template Class

As set out in Design Patterns, the Template pattern has four kinds of methods that you can make use of in derived classes:

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

  2. Methods that are not filled in at all and must be implemented in derived classes. In Java, you would declare these as abstract methods, and that is how they are referred to in the pattern description.

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

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

Template Method Patterns in Java

Java abounds with Template patterns. A large number of classes start out with an abstract class with only a few methods filled in and have subclasses where these methods are implemented.

AbstractTableModel

The TableModel interface defines all of the required methods for the data class for handling data for a JTable display. The AbstractTableModel provides default implementations for all but three of these methods. When you create the data class, you must provide implementations for getRowCount(), getColumnCount(), and getValueAt(int row, int column).

AbstractBorder

The Border interface defines the methods for a border to surround any JComponent. The AbstractBorder class provides default implementations of all of these methods for a border having no width. You can then create your own border subclasses by reimplementing the AbstractBorder methods to handle an actual border.

AbstractListModel

Similar to the TableModel, the AbstractListModel provides a default implementation of the ListModel interface. You can create your own list data models by subclassing AbstractListModel. You can also use the DefaultListModel class as a simple Vector data repository.

Sample Code

Let's consider a program for drawing triangles on a screen. We start with an abstract Triangle class and then derive some special triangle types from it, as shown in Figure 25.1.

The abstract Triangle class and three of its subclasses.

Figure 25.1. The abstract Triangle class and three of its subclasses.

Our abstract Triangle class illustrates the Template pattern.

public abstract class Triangle {
    private Point p1, p2, p3;
    //--------------
    public Triangle(Point a, Point b, Point c) {
        //save
        p1 = a; p2 = b; p3 = c;
    }
    //--------------
    public void draw(Graphics g) {
        //This routine draws a general triangle
        drawLine(g, p1, p2);
        Point current = draw2ndLine(g, p2, p3);
        closeTriangle(g, current);
    }
    //--------------
    public void drawLine(Graphics g, Point a, Point b) {
        g.drawLine(a.x, a.y, b.x, b.y);
    }
    //--------------
    //this routine is the "Hook" that has to be implemented
    //for each triangle type.
    abstract public Point draw2ndLine(Graphics g,
                    Point a, Point b);
    //--------------
    public void closeTriangle(Graphics g, Point c) {
        //draw back to first point
        g.drawLine(c.x, c.y, p1.x, p1.y);
    }
}

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. In that way, the derived class can move the third point to create the kind of rectangle that you wish to draw.

This is a general example of a class's 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 Point draw2ndLine(Graphics g, Point a, Point b) {
        g.drawLine(a.x, a.y, b.x, b.y);
        return b;
    }

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 implement the draw2ndLine method in a derived stdTriangle class.

public class stdTriangle extends Triangle {
    public stdTriangle(Point a, Point b, Point c) {
        super(a, b, c);
    }
    public Point draw2ndLine(Graphics g, Point a, Point b) {
        g.drawLine(a.x, a.y, b.x, b.y);
        return b;
    }
}

Drawing an Isoceles Triangle

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

public class IsocelesTriangle extends Triangle {
    private Point newc;
    private int nexcx, newcy;
    private int incr;

    public IsocelesTriangle(Point a, Point b, Point c) {
        super(a, b, c);
        double dx1 = b.x - a.x;
        double dy1 = b.y - a.y;
        double dx2 = c.x - b.x;
        double dy2 = c.y - b.y;

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

        if (side2 < side1)
                incr = -1;
        else
                incr = 1;
        double slope = dy2 / dx2;
        double intercept = c.y - slope* c.x;

        //move to point c so that this is an isoceles triangle
        newcx = c.x; newcy = c.y;
        while (Math.abs(side1 - side2) > 1) {
            newcx += incr;    //iterate a pixel at a time
            newcy = (int)(slope* newcx + intercept);
            dx2 = newcx - b.x;
            dy2 = newcy - b.y;
            side2 = calcSide(dx2, dy2);
        }
        newc = new Point(newcx, newcy);
}
//--------------
//calculate the length of the side
private double calcSide(double dx, double dy) {
    return Math.sqrt(dx*dx + dy*dy);
}

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 that it will draw the closing side of the triangle correctly.

//draws a second line using the saved new point
    public Point draw2ndLine(Graphics g, Point b, Point c) {
        g.drawLine(b.x, b.y, newc.x, newc.y);
        return newc;
    }
}

The Triangle Drawing Program

The main program creates instances of the triangles that we want to draw and adds them to a Vector in the TPanel class.

public class TriangleDrawing extends JxFrame {
    private stdTriangle t, t1;
    private IsocelesTriangle it;

    public TriangleDrawing() {
        super("Draw triangles");
        TPanel tp = new TPanel();
        t = new stdTriangle(new Point(10,10), new Point (150,50),
                    new Point(100, 75));
        it = new IsocelesTriangle(new Point(150,100),
                    new Point(240,40), new Point(175, 150));
        t1 = new stdTriangle(new Point(150, 100),
                    new Point(240,40), new Point(175, 150));
        tp.addTriangle(t);
        tp.addTriangle(it);
        getContentPane().add(tp);
        setSize(300, 200);
        setBackground(Color.white);
        setVisible(true);
        }

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

public class TPanel extends JPanel {
    private Vector triangles;
    public TPanel() {
        triangles = new Vector();
    }
    public void addTriangle(Triangle t) {
        triangles.addElement(t);
    }
    public void paint(Graphics g) {
        for (int i = 0; i < triangles.size(); i++) {
            Triangle tng1 = (Triangle) triangles.elementAt(i);
            tngl.draw(g);
        }
    }
}

Figure 25.2 offers an example of two standard triangles in the left window and the same code using an isoceles triangle in the right window.

Two standard triangles (left) and a standard triangle and an isoceles triangle (right).

Figure 25.2. Two standard triangles (left) and a standard triangle and an isoceles triangle (right).

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 the base Triangle class, we see that there are three method calls:

     drawLine(g, p1, p2);
     Point current = draw2ndLine(g, p2, p3);
     closeTriangle(g, current);

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, they appear to be called from the base class.

If this idea makes you uncomfortable, you will probably take solace in recognizing that all of the method calls originate from the derived class and that those calls move up the inheritance chain until they find the first class that implements them. If this class is the base class, fine. If it is not, then it could be any other class in between. Now, when we 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, although it does sort of seem that way.

Consequences of the Template Pattern

Template patterns occur all of 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.

  1. The first significant point is that your base class may define only some of the methods that it will be using, leaving the rest to be implemented in the derived classes.

  2. The second point is that there might 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 might not be worked out completely in the base class.

  3. Template classes often have some abstract methods that you must override in the derived classes. They might also have some classes with a simple placeholder implementation that you are free to override where appropriate. If these placeholder classes are called from another method in the base class, then these overridable methods are called Hook methods.

Thought Question

  1. The Abstract Collection class in the java.util package is an example of a Template pattern. To use a collection derived from it, you must provide an implementation of the iterator and size methods. Write a program utilizing a collection derived from AbstractCollection.

Programs on the CD-ROM

ProgramDescription

TemplateTriangleDrawing.java

Draws generic and isoceles triangles using the Triangle template pattern.
..................Content has been hidden....................

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