Polymorphism in Components
Polymorphism is the ability for classes to provide different implementations of methods that are called by the same name. Polymorphism allows a method of a class to be called without regard to what specific implementation it provides. For example, you might have a class named Road which calls the Drive method of an additional class. This Car class may be SportsCar, or SmallCar, but both would provide the Drive method. Though the implementation of the Drive method would be different between the classes, the Road class would still be able to call it, and it would provide results that would be usable and interpretable by the Road class.
Polymorphism in components can be implemented in a variety of ways:
- Interface polymorphism - Multiple classes may implement the same interface, and a single class may implement one or more interfaces. Interfaces are essentially definitions of how a class needs to respond. An interface describes the methods, properties, and events that a class needs to implement, and the type of parameters each member needs to receive and return, but leaves the specific implementation of these members up to the implementing class.
- Inheritance polymorphism - Multiple classes may inherit from a single base class. By inheriting, a class receives all of the methods, properties, and events of the base class in the same implementation as the base class. Additional members can then be implemented as needed, and base members can be overridden to provide different implementations. Note that an inherited class may also implement interfaces — the techniques are not mutually exclusive.
- Polymorphism through abstract classes - Abstract classes provide elements of both inheritance and interfaces. An abstract class is a class that cannot be instantiated itself; it must be inherited. Some or all members of the class might be unimplemented, and it is up to the inheriting class to provide that implementation. Members that are implemented might still be overridden, and the inheriting class can still implement addition interfaces or other functionality.
These three types of polymorphism are discussed in greater detail below.
A powerful technique in component programming is being able to implement multiple interfaces on an object. Each interface is composed of a small group of closely related methods, properties, and events. By implementing interfaces, your component can provide functionality to any other component requiring that interface, without regard to the particular functionality contained within. This allows successive versions of components to incorporate different capability without disturbing core functionality.
The features of your component most commonly used by other developers should naturally be members of the component class itself. However, components with a large number of members can be difficult to use. Consider factoring out some features of your component as separate interfaces that are implemented privately.
Another benefit of defining features in terms of interfaces is that you can add features to your component incrementally by defining and implementing additional interfaces. Advantages include:
- Design effort is simplified, because components can begin small, with minimal functionality, and continue to provide that functionality while acquiring additional features over time, as it becomes clear from actual use what those features should be.
- Maintaining compatibility is simplified, because new versions of a component can continue to provide existing interfaces, while adding new interfaces. Subsequent versions of client applications can take advantage of these when it makes sense for them to do so.
Polymorphism Through Inheritance
Visual Basic and C# also provide polymorphism through inheritance. This is a powerful mechanism for small-scale development tasks, but has generally proven to be problematic for large-scale systems. An over-emphasis on inheritance-driven polymorphism typically results in a massive shift of resources from coding to designing, which does nothing to shorten overall development time.
Given that the real test of software is whether it works for end users, tools for rapid prototyping and rapid application development (RAD) have gained wider acceptance than tools for object-oriented programming.
When to Use Inheritance-Driven Polymorphism
The foremost use of inheritance is to add functionality to an existing base class. Programmer productivity is much greater when you start with a framework of fully debugged base classes, and methods can be incrementally added to base classes without breaking versioning.
You may also wish to use inheritance when your application design includes several related classes that must share identical implementation for certain common functions. The overlapping functionality can be implemented in a base class, from which the classes used in the application can be derived. An abstract class combines features of both inheritance and implementation, and may be useful when elements of each are needed.
Polymorphism Through Abstract Classes
Abstract classes provide features of both inheritance and interface implementation. An abstract (MustInherit in Visual Basic) class is a class that cannot be instantiated, and must be implemented in an inheriting class. It may contain methods and properties that are already implemented, but it may also incorporate unimplemented procedures which must be implemented in the inheriting class. This allows you to provide an invariant level of functionality in some methods of your class, while leaving flexibility options open for other procedures. An additional benefit of abstract classes is that when new versions of your component are required, additional methods may be added to the base class as needed, whereas interfaces must remain invariant.
When to Use Abstract Classes
An abstract class is useful when you need a group of related components to incorporate a set of methods with identical functionality, but also require flexibility in the implementation of other methods. Abstract classes are also valuable when versioning issues are anticipated, because the base class remains flexible and easily modified. For details, see Recommendations for Abstract Classes vs. Interfaces.