
Interchangeable Derived Classes
Derived classes in a class hierarchy can sometimes be used interchangeably with their base class, a process called inheritance-based polymorphism. This approach combines the best features of interface-based polymorphism with the option of reusing or overriding code from a base class.
An example where this can be useful is in a drawing package. For example, consider the following code fragment, which does not use inheritance:
Sub Draw(ByVal Shape As DrawingShape, ByVal X As Integer, _
ByVal Y As Integer, ByVal Size As Integer)
Select Case Shape.type
Case shpCircle
' Insert circle drawing code here.
Case shpLine
' Insert line drawing code here.
End Select
End Sub
This approach poses some problems. If someone decides to add an ellipse option later, it will be necessary to alter the source code; it is possible that your target users will not even have access to your source code. A more subtle problem is that drawing an ellipse requires another parameter (ellipses have both a major and a minor diameter) that would be irrelevant to the line case. If someone then wants to add a polyline (multiple connected lines), then another parameter would be added, and it would be irrelevant to the other cases.
Inheritance solves most of these problems. Well-designed base classes leave the implementation of specific methods up to the derived classes, so that any kind of shape can be accommodated. Other developers can implement methods in derived classes by using the documentation for the base class. Other class items (such as the x- and y-coordinates) can be built into the base class because all descendants use them. For example, Draw could be a MustOverride method:
MustInherit Class Shape
Public X As Integer
Public Y As Integer
MustOverride Sub Draw()
End Class
Then you could add to that class as appropriate for different shapes. For example, a Line class might only need a Length field:
Class Line
Inherits Shape
Public Length As Integer
Overrides Sub Draw()
' Insert code here to implement Draw for this shape.
End Sub
End Class
This approach is useful because other developers, who do not have access to your source code, can extend your base class with new derived classes as needed. For example, a class named Rectangle could be derived from the Line class:
Class Rectangle
Inherits Line
Public Width As Integer
Overrides Sub Draw()
' Insert code here to implement Draw for the Rectangle shape.
End Sub
End Class
This example shows how you can move from general-purpose classes to very specific classes by adding implementation details at each level.
At this point it might be good to reevaluate if the derived class truly represents an "is a" relationship, or instead is a "has a" relationship. If the new rectangle class is just composed of lines, then inheritance is not the best choice. However, if the new rectangle is a line with a width property, then the "is a" relationship is maintained.