How to write component interfaces in C++
"The most important thing to get right is the interface. Everything else can be fixed later. Get the interface wrong, and you may never be allowed to fix it." [Sutter, 2004]
1. Seperate interface from implementation
1.1 Pimpl idom (private implementation)
This idom is also known as Bridge pattern [GoF, 1994]. The interface (known as Handle class) have only one private member variable, a pointer to the impelmentation (known as the Body). The functionality in the interface forward function calls to the implementation. This pattern is preferable for "old" interfaces since it minimize client changes and C++ where it remove build dependencies.
1.2 Abstract class (protocol class)
Two methods to create the interface class: 1. Use Factory method pattern [2] to create an instance of the concrete implementation and return a base class pointer to the abstract class (interface class). 2. Provide a static method in the interface this also create the right instance and return a base class pointer. When there are several different implementations for the interface or many clients that use the same interface, a factory method can be used. The factory method separates the creation of the interface from the use of it. The user of the interface has no knowledge or logic of which concrete object they use. When there are only one implementation the static method will keep the design simpler.
In both cases should the interface not consist of any data or implementation of any functionality. It has been a disscussion to allow simple implementation in the interface. Assess this for relevant cases.
Some consequneces are:
Pros:
- minimalization of compile time dependencies between files.
- extensible component design.
- clean and simple interface, easy to use.
Cons (insiqnificant):
- extra use of memmory.
- overhead for use of virtual methods.
2. Dependencies
2.1 Use forward declartions
If possible all datatypes is forward declared.
Exception, with inheritance base class must always be included.
2.2 Use of #include in header files
Only header files from standard library and other third part API's should be included. This librarys have stable interfaces and rarly changed.
3. Utility functions in interfaces
If the interface consists only of utility functions in a namespace, could the functions be grouped and divided into separate files. This lead to more resilience with regards to implementation changes. Files and functions can still belong to the same namespace.
4. Files to include for using the interface
The interface documentation should list which files the client need to include when it make use of the interface. One suggestion is to create an own header file for every interface that contains the include files the client needs to yutilize the interface.
5. Document interface
Yes!
6. Interface for Abstract class
Abstract classes provide common definitions (implementation) for members and methods that sub-classes inherits. The abstract class can also be the clients interface to make use of the polymorphism mechanisme. But then the abstract class has two responsiblities, it is the container for common functionalities in the sub-classes and the contract for the clients. With this design we have no guarantee for a stable interface and the abstract class has two responsibilities, thats not good design! So a better design solution will be the abstract class inherit from an interface that provides the contract for the clients, and then the abstract class only contains common functionality for sub-classes.
7. References
[1]
[2] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns - Elements of Reusable Object-Oriented Software (GoF), 1994.