Base Class Design Changes After DeploymentÂ
Ideally, class hierarchies never change after deployment, because even small changes risk unintended consequences. In reality, such changes are sometimes unavoidable: product requirements can change, and design specifications sometimes omit key elements. One category of inheritance problems is called the fragile base class problem.
The Fragile Base Class Problem
The main drawback to using inheritance hierarchies is a problem called fragile base classes. Changes to base classes often require the base class and all the code in derived classes to be changed, recompiled, and redistributed. This problem is even more difficult when multiple developers author the base class and derived classes, because each party may deny access to their source code. In a worst-case scenario, a customer might inadvertently use the compiled binary form of an updated base class with the original and incompatible binary version of the derived classes.
Base class changes that can potentially break derived classes include overriding or changing the data type of base class members.
Minimizing Fragile Base Class Problems
The best way to avoid the fragile base class problem is to make changes only in derived classes. However, this is not always possible because it requires you to consider every possibility when the base class is first released; despite one's best efforts, unforeseeable changes to the base class are sometimes inevitable.
Using MustInherit classes and MustOverride methods help reduce fragile base class problems because doing so moves implementation details to derived classes and reduces the need for base class changes. Again, however, this is not always possible, even with the best planning.
Shadowed data members in derived classes are also helpful, because they reduce naming conflicts with base class members.
The safest way to extend the functionality of a base class is to add new members. The only way that new members can break a base class is if the derived class uses the Overloads keyword to inherit methods from the base class and introduce new methods with the same name. This problem can be avoided by explicitly specifying in the derived class what base class methods you want to inherit from the base by redefining those methods and delegating to them.
In a sense, all base classes are fragile to some degree. In the final analysis, the fragile base class problem cannot be eliminated, but it can be minimized through careful design of class hierarchies that reduces the need for base class changes, and through thorough testing when such changes are unavoidable.