Generally speaking, in object-oriented programming, an interface is an entity that describes a set of method signatures, whose implementations can be provided by multiple classes that would otherwise not necessarily be related to each other. An interface is thus a contract that must be fulfilled by the classes that implement the interface itself.
Interfaces make it possible to achieve multiple inheritance and to apply the concept of abstraction in the design of classes and software modules (a concept we saw in the post on SOLID principles).
But now let’s refer to LabVIEW. In the LabVIEW world, interfaces are a fairly recent concept. In fact, they were introduced with the 2020 version.
As stated in the documentation, in LabVIEW an interface can be viewed as a class without data, that is, without the private data cluster. An interface declares a role that an object can perform without defining how to perform that role. The declaration of that role is done by defining a set of empty dynamic methods within the interface itself.
Inheriting from an interface, a class declares that its objects perform that role, and the class becomes responsible for specifying how that role is implemented. The class must override all the dynamic methods defined in the interface it implements.
In LabVIEW as well, classes can inherit from multiple interfaces thus allowing multiple inheritance. When a class inherits from multiple interfaces, the class must implement all the methods of all the interfaces it implements.
Abstract classes and interfaces
First, let us define what is meant by an abstract class. In general, an abstract class is a class that cannot be instantiated and must therefore be extended with derived classes. An abstract class defines methods and properties that are also entirely or partly abstract (empty).
Given that (pure) abstract classes in LabVIEW do not exist, we can summarily see an abstract class as a base class (super class) with empty dynamic methods and properties and where dynamic methods are marked with the Descendants must override option. I talk about abstract classes and not base classes or superclasses simply to have a more theoretical approach. Note that all the considerations in this subchapter can also be applied to hybrid base classes (hence not purely abstract).
That premise having been made, let us understand the similarities and differences between abstract classes and interfaces.
Both represent a contract that the derived class must fulfill by implementing the methods of the base type (i.e., of the class it extends or the interface it implements). Abstract classes, however, define a stronger bond with the derived class since they represent its base type. Interfaces, on the other hand, define a generic model (a role) that represents a common behavior for the classes that implement those interfaces. Such classes can be of various kinds and nature. Consider, for example, the Serialize method of the Serializable interface. This interface represents the ability of the classes that implement it to serialize themselves into a sequence of bytes. This interface could be implemented by classes that are completely unrelated to each other: for example, the DMM class that models a digital multimeter, the TestReportSettings class that contains the settings for generating reports for a test sequence, and the User class that contains user’s data.
So when to use an abstract class and when to use an interface?
The only answer I feel I can give is…it depends on a case-by-case basis and depends on the developer’s intentions. The two in fact, although they have several similarities, have quite different roles from the point of view of software design. An abstract class defines a kind of vertical contract with derived classes and its use limits the level of abstraction of the software components that use it. This is because the use of an abstract class places a constraint on the class hierarchy. An interface, on the other hand, represents more of a horizontal contract with the objects that will implement it. This contract is more generic than that of the abstract class. Because of this, however, an interface creates a stronger level of abstraction than an abstract class and allows the various software modules to have more generic dependencies. Thus, if we want to opt for a more abstract, generic and extensible approach, we will opt for an interface. Conversely, if we want a stronger constraint on the class hierarchy (potentially losing in abstraction), we will opt for an abstract class.
A general rule of thumb we can use to decide whether to use an abstract class or an interface is: use abstract classes and inheritance if the statement “A is a B” is true. Use interfaces if the statement “A is capable of…” is true. For example, we can say that a rectangle is a polygon but it makes no sense to say that a rectangle is capable of being a polygon. In any case, I believe that common sense is always the rule that beat all others. Sometimes in fact an interface will fit much better, even if the rule above says otherwise.
Best practices for naming interfaces
National Instruments provides some guidelines in naming interfaces:
- Use an adjective or adverb that describes the capability of an object. For example, name an interface CanMeasureVoltage.lvclass if the interface represents hardware that can measure voltage. Any class or interface that inherits that interface is capable of measuring voltage. Another example would be to name an interface Serializable if the interface represents the ability of the classes that implement it to serialize themselves into a sequence of bytes (such as binary, XML, or JSON serialization).
- Where an adjective or adverb cannot be used, use a name that describes the category of classes that inherit from the interface. For example, name an interface Database.lvclass if the interface describes a category of classes that have the role of saving data to different databases.
- Avoid using the initial capital letter “I.” Although most textual programming languages name interfaces with an initial capital letter “I” to differentiate interfaces from classes, LabVIEW distinguishes interfaces and classes using glyphs.
Benefits of using interfaces
The use of interfaces provides several benefits:
- An interface allows defining a behavior that can be implemented by a group of unrelated classes without forcing them to share a common class hierarchy.
- By implementing an interface, a class can play a different role other than that dictated by its class hierarchy.
- Because a class is not limited to the number of interfaces it can implement, it can participate in a wide variety of roles.
- A class can be exposed through its interface, effectively hiding its implementation details by forcing all communication through its public interface methods.
- Existing systems can be easily modified (or better extended) to provide new functionalities within their current class hierarchy
The next post will illustrate a practical example on the use of interfaces and classes. I hope it will provide more insights and tools for applying these concepts to real problems.
See you in the next post.