6.9 Refactoring of Base Classes
Refactoring a base class is often required to preserve the base class's information-hiding properties of the base class. Information hiding is a software engineering principle stating that knowledge of design decisions (the information) should have the narrowest possible dissemination (the hiding). This principle helps to insure the independence of different parts of the system. Furthermore, information hiding promotes easier maintenance and adaptation of software, because a change in a design decision does not have a widespread impact if information about that design decision has been circumscribed. However, if many, or seemingly unrelated, parts of the system are conditioned on knowledge of that design decision, then a change in the design decision may render invalid these other parts of the system.
As an example, the DisplayableNumber class must be refactored to accomodate an OctalNumber class because the Show method is inappropriate for the new class. An OctalNumber object is just like a Number object, except that the OctalNumber object's value is displayed using octal-number notation and not as a decimal value. For example, an OctalNumber object with the decimal value 23 would be displayed in its octal representation as 27. While most of the methods in the Number and DisplayableNumber classes are exactly those needed to implement an OctalNumber, the Show method is inappropriate because it displays values in a decimal notation.
Simply overriding the Show method in the OctalNumber class leads to a loss of information hiding. A first approach to modifying the Show method is to provide an overriding Show method: the inherited Show method must be replaced (overriden) by a Show method that displays the value in octal notation. However, overriding the Show method leads to a dilemma regarding the sharing of data between the base and derived classes. The Show method uses the TextBox variable to display the DisplayableNumber's current value. To override the Show method in a derived class, the TextBox variable must be placed in the protected region of the base class. This change, while workable, significantly enlarges the scope (i.e., weakens the protection) of the TextBox variable, as the base class no longer completely encapsulates the existence of the TextBox variable. Therefore, both the base class and its derived classes are dependent on the following design decisions:
If any of these design decisions change, then both the base class and all of its derived subclasses are vulnerable to change. Notice how serious a problem this is: a derived class (OctalNumber) may need to be changed because of a change in a class (TextBox).
The cause of the problem is that the Show method performs two related, but separable, actions, which are formatting the character string to be displayed, and displaying the string in the TextBox. The design dilemma is easily resolved by recognizing that the OctalNumber class only needs to override the first action, and only the second action requires knowledge of the TextBox class and the TextBox variable. A new virtual method, char* Format(), is added to the base class to perform the first action. The revised Show() method will use the Format method.
Separating the two actions of the Show method leads to the following revised definition and implementation of the DisplayableNumber class. Notice that the Format method is a virtual method, meaning that an overriding of this method by a derived class will be used in preference for the default (decimal formatting) given in the DisplayableNumber class.
Note, however, this refactoring of the base class is incomplete because the Reset method has not been accounted for. When Reset is called, it is expected to produce a new value for the DisplayableNumber by parsing the string obtained from the TextBox. However, this parsing is done assuming that the string represents a decimal value. Once again, this is inappropriate for the OctalNumber class because the string "77" parsed as a decimal value is clearly different from this same string parsed as an octal value.
In the Reset method there are found again two separable actions: obtaining from the user the string that represents the new value, and parsing this string to determine the new value Following the principle of information hiding, the first of these actions will remain the private concern of the base class. The second action will be established as a virtual method in the protected section of the base class. The additional changes needed in the DisplayableNumber are shown in the figure below.
Using this approach, implementing the OctalNumber class becomes straightforward, the code for which is shown below. Since OctalNumber inherits from Number, the only method that the OctalNumber must implement is the Format method. The Format method is declared in the protected section of the OctalNumber class so that it does not become a part of the public interface of this class.