Many high-level languages support inheritance and interfaces, and for someone new to the concepts, it’s sometimes not clear which one to choose. Although languages differ in their exact handling of inheritance and interfaces, the basics are usually the same, so this tip should be valid for most languages.
Inheritance means that we derive one class (the derived class) from another class (the base class).
The derived class is an extension of the base class. It contains all the features (methods and data members) of the base class, can extend it with new features, and can reimplement virtual methods of the base class.
Some languages, like C++, support multiple inheritance, where a derived class can have multiple base classes, but usually inheritance is restricted to a single base class.
Interfaces can usually only define methods and no data members (but C# for example allows data members in the form of properties within interfaces), and a class can always implement multiple interfaces.
An interface contains only method definitions without implementations, and a class that implements an interface supplies the implementation.
So, using inheritance, you write a base class with method implementations, and when you derive a class from it, this class will inherit everything from the base class, and is immediately able to use its features.
An interface on the other hand is just a contract of method signatures, and a class that wants to implement an interface is forced to supply the implementations for all methods of the interface.
So when do you use which?
In some cases, the language already dictates what you use: if you need your class to have multiple ‘parents’, you cannot use inheritance in languages that don’t support multiple inheritance. And if you want to reuse a library object, you have to use the fitting concept, depending on if that library object is a class or an interface.
But which to use if you are free to choose?
Basically, base classes describe and implement common behavior of related types, while interfaces describe functionality that unrelated types can implement. Inheritance describes ‘is a’ relationships, interfaces describe ‘behaves like’ relationships.
For example, say that you are writing a flight simulator. Your basic entity, which you will for example store in a list, will be ‘Airplane’. Your concrete types will be ‘Concorde’ and ‘Phantom’. So how should you model the three types?
Concorde and Phantom are related, they both are airplanes and share data, like ‘Weight’ or ‘MaxSpeed’ and functionality, like ‘Accelerate’, so we can model them with inheritance. ‘Airplane’ will be the base class with common data and methods, and ‘Concorde’ and ‘Phantom’ will derive from ‘Airplane’. We could say that both are specialized airplanes, which is why it’s often said that inheritance means specialization.
Now assume that we also add a class ‘Pilot’ to our program, and we want to give the user the ability to save the game and load it later. So when he saves the game, we need to save the state of all Aircrafts and the state of all Pilots. And we want to do this in one function that takes just a list of all saveable objects. So how do we model this?
To answer this, we must take a look at the different types we want to save. Pilots and Airplanes. It’s obvious that they are not related at all. They share no common data and no common functionality. We can see that writing a base class ‘Saveable’ and derive both Pilot and Airplane from it would make little sense, since no code in Saveable could be reused by Airplane or Pilot, since both have no common properties. In this case, an interface is the best solution. We can write an interface ‘ISaveable’ with a method Save(). Pilot could then implement ISaveable.Save() by saving his name, while Airplane could save its current speed and coordinates.
As you can see, a clear image of the relationship between classes often makes the choice clear:
Use inheritance for related types, where each derived class ‘is a’ base class.
Use interfaces for unrelated types which have some common functionality.
Here are some more points to consider with inheritance and interfaces:
– Interfaces are fixed. When you change an interface, you have to change every class implementing that interface. But when you change a base class, every derived class will gain the new functionality, which can both be good (if you make a bugfix in some base class method implementation, a derived class using that method will gain the bugfix without you needing to change it) or bad (if a change in the baseclass introduces a new bug, all derived classes using the method will be bugged too).
– Interfaces are usually more flexible, since in most languages you can only derive from one class, but implement many interfaces
– Interfaces help to protect internal classes: Assume class A has an internal object b of class B. When a method in A returns a pointer or reference to b, the code that called this method now has access to the whole object b, which can be dangerous if A only wants to expose certain members of b. This problem can be solved if you create an interface I with just the members which are safe to expose. When B implements this interface, and your method in A returns b via an I pointer or reference, the outside code can only do what you allow through the interface.