How to Build Solid, Successful Client Relationships
July 12, 2021
Foundations in Software Architecture and Design Patterns: The Interface
Class based inheritance is what is most commonly meant when using C#’s semantics to inherit from one class to another. It is not uncommon for a base class to become polluted with “catch-all” methods and loose its singular focus. Long inheritance chains are difficult to maintain and lead to a fragile software architecture.
This article will demonstrate how to use a common abstraction in C# called an Interface. This topic is foundational in understanding how many other design patterns work.
Classes Based Inheritance
A typical C# application is comprised of many custom types that the Developer has written. One such type is called a Class. The Class can provide the functionality that is required by the application, and, can be used by other Classes within the application. Classes expose both properties and methods. Properties provide information about the class (for example “Name”) while methods perform actions.
One class can also inherit from another class, known as “Class Based Inheritance.” When a class inherits from a “base class,” C# can treat that class as if it were the base class. The following demonstrates two musical instrument classes, the Trombone and Violin, inheriting from base class called Instrument:
The base class, Instrument, is not very interesting. In fact, its single method, “PlayNote” doesn’t contain any implementation. It does, however, make sense if you consider that every type of musical instrument is played differently. The violin, for example, is comprised of four strings. The player uses a combination of bow movement and finger placement across the four strings to produce a note. The Trombone is completely different: it uses wind, buzzing lips and slide positioning to create sounds.
If the Instrument class were to provide implementation for every type of instrument, the “PlayNote” method would be huge. Using Class Based Inheritance, both the Violin and Trombone can be treated as if it were the Instrument class while providing a specific implementation for “the PlayNote” method.
A different approach would be to use an Interface definition.
An Interface differs from a class in that an Interface only provides the signature of Properties and Methods without any implementation. A class provides the “What” and the “How”, but an Interface only provides the “What.”
A Class is said to “implement” an interface as opposed to “inherits.” When the Class implements an interface, it has to provide an implementation for each of the properties and methods that is defined by the Interface. Once a Class has implemented an Interface, it can then be treated as the interface.
Finally, a Class can only inherit from one base class, but, it can implement many interfaces.
The following example demonstrates a refactoring of the Instrument class into the “IInstrument” interface. Prefixing an Interface with the letter “I” is a common naming convention. Additionally, a second interface is introduce: the ITuneInstrument.
As with PlayNote, each instrument has a different method of tuning. The following example illustrates how the Trombone or Violin can now be passed to a method that defines an ITuneInstrument parameter. Because both classes implement ITuneInstrument, instances can be treated as that type. However, the method “TuneInstrument” only knows about the “ITuneInstrument” and not the “IInstrument” interface.
This article has examined the differences between Classes and Interfaces and has illustrated Class Based Inheritance and Interface Implementation. What this is leading to is Dependency Management. Often, the term “spaghetti” code is used to describe software that is all over the place. It is often described as being fragile (changing one portion of code seemingly affects another area), rigid and hard to change.
The previous example, the Orchestra’s Tune Instrument method illustrated that ANY class can be passed to this method as long as it implements the expected interface. One of the challenge with writing good software is that change happens. When code depends on concrete implementations of a class, it becomes tied to that specific type. Depending on Interfaces defines expectations and this is the basis of many design patterns and principles.