Template Method



Item 22. Template Method

The Template Method pattern has nothing whatsoever to do with C++ templates. Rather, it's a way for a base class designer to give clear instructions to derived class designers concerning how the base class contract may be implemented (see Polymorphism [2, 3]). However, even if you think this pattern should go by a different name, please continue to use the standard name "Template Method." Much of the benefit of using patterns derives from the standard technical vocabulary they establish (see Design Patterns [3, 7]).

A base class specifies its contract to the world at large through its public member functions and specifies additional details for classes derived from it through its protected member functions. Private member functions may also be used as part of the implementation of a class (see Assignment and Initialization Are Different [12, 41]). Data members should be private, so we'll leave them out of this discussion.

The decision as to whether a base class's member function should be nonvirtual, virtual, or pure virtual is driven primarily by considering how the behavior of that function is to be customized by derived classes. After all, code that uses a base class's interface doesn't really care how a particular operation is implemented by the object; it wants to perform an operation on the object, and it's up to the object to implement that operation appropriately.

If a base class member function is nonvirtual, the base class designer is specifying an invariant over the hierarchy rooted at the base class. Derived classes should not hide a base class nonvirtual function with a derived class member of the same name (see Member Function Lookup [24, 87]). If you don't like the contract specified by the base class, find a different base class. Don't attempt to rewrite its contract.

Virtual and pure virtual functions specify operations whose implementations can be customized by derived classes through overriding. A non-pure virtual function provides a default implementation and does not require overriding whereas a pure virtual function must be overridden in a concrete (that is, nonabstract) derived class. Either kind of virtual function allows a derived class to plug replace its entire implementation while preserving its interface.

Use of a Template Method gives the base class designer an intermediate level of control between the "take it or leave it" nonvirtual function and the "if you don't like it, replace the whole thing" approach of a virtual function. A Template Method fixes the overall structure of its implementation but defers some portion of its implementation to derived classes. Typically, a Template Method is implemented as a public, nonvirtual function that calls protected virtual functions. The derived classes must accept the overall implementation specified in the inherited, nonvirtual base class function but may customize its behavior in limited ways by overriding the protected virtual functions it invokes.

class App {
  public:
    virtual ~App();
    //...
    void startup() { // Template Method
        initialize();
        if( !validate() )
            altInit();
    }
  protected:
    virtual bool validate() const = 0;
    virtual void altInit();
    //...
  private:
    void initialize();
    //...
};

The nonvirtual startup Template Method calls down to customizations provided by derived classes:

class MyApp : public App {
  public:
    //...
  private:
    bool validate() const;
    void altInit();
    //...
};

Template Method is an example of the Hollywood Principle at work; that is, "Don't call us; we'll call you" (see Commands and Hollywood [19, 67]). The overall flow of the startup function is determined by the base class, and startup is invoked by clients of the base class's interface, so derived class designers don't know when validate or altInit will be called. But they do know what validate and altInit should do when they are called, and together the base and derived classes cooperate to produce a complete function implementation.