I l@ve RuBoard Previous Section Next Section

5.7 How Abstract Should a Base Class Be?

Under our current design, the abstract base class provides an interface but does not provide an implementation. Each derived class must not only provide the unique algorithm for generating its elements but also provide support for finding the element, printing the element, maintaining the length and beginning position of the sequence object, and so on. Is this a bad design?

If the designer of the abstract base class is also providing the derived numeric sequence classes and if they are not expected to be added to very often, this design works quite well. If, however, a primary activity is the delivery of new numeric sequence classes and if that activity has been farmed out to individuals who are more comfortable with mathematics than programming, this design complicates the delivery of each derived class.

The following alternative base class design factors implementation support of the shared derived class data into the base class. The interface is unchanged. The program of the preceding section need not be modified, although it must be recompiled. The design change simplifies the work necessary to provide a derived class.

Here is the revised num_sequence base class definition. The two data members ?_length and _beg_pos ?are now num_sequence data members. We declare them as protected to allow the derived classes direct access to them. The supporting access member functions ?length() and beg_pos() ?are now also num_sequence class members. We declare them as public to allow the general program read access to these values.

A new data member has also been added to the num_sequence class. _relems, a reference to a vector of integers, refers to the static vector of the derived class.Why is it declared a reference rather than a pointer? As we say in Section 2.3, a reference can never refer to a null object, whereas a pointer may or may not be null. By making it a reference, we spare ourselves from having to check for a null pointer.

A reference data member must be initialized within the constructor's member initialization list and, once initialized, can never be changed to refer to a different object. A pointer data member has neither restriction: We can either initialize it within the constructor or initialize it to null and assign it a valid address later. We choose between making a data member a reference or pointer based on these characteristics.

The base class now has all the information necessary to search and display the elements of the numeric sequence. This also allows us to redefine elem() and print() as public members of num_sequence.



class num_sequence { 


public: 


   virtual ~num_sequence(){} 


   virtual const char* what_am_i() const = 0; 





   int                 elem( int pos ) const; 


   ostream&            print( ostream &os = cout ) const; 





   int                 length()  const { return _length;  } 


   int                 beg_pos() const { return _beg_pos; } 


   static  int         max_elems()     { return 64; } 





protected: 


   virtual void        gen_elems( int pos ) const = 0; 


   bool                check_integrity( int pos, int size ) const; 





   num_sequence( int len, int bp, vector<int> &re ) 


      : _length( len ), _beg_pos( bp ), _relems( re ){} 





   int                _length; 


   int               _beg_pos; 


   vector<int>       & _relems; 


}; 

Each derived numeric sequence class now must program only those things unique to it: gen_elems(), which computes the elements of the sequence; what_am_i(), which identifies the sequence; the static vector to hold the sequence elements; and a constructor. The derived sequence class inherits the members for finding the element, printing the element, and maintaining the length and beginning position. For example, here is our revised Fibonacci class definition:



class Fibonacci : public num_sequence { 


public: 


   Fibonacci( int len = 1, int beg_pos = 1 ); 


   virtual const char* what_am_i() const 


           { return "Fibonacci"; } 





protected: 


   virtual void       gen_elems( int pos ) const; 


   static vector<int> _elems; 


}; 

Although num_sequence remains an abstract base class, it now provides a partial implementation that is inherited by each derived class.

    I l@ve RuBoard Previous Section Next Section