Select Page

Interface designs and their implementations – Part 1: Structure and definition

The use of software interfaces is a fundamental tool for developing durable and robust software architectures. For this reason, they should be established in the architecture as early as possible to stabilize it. This allows the software architect to ensure the rapid distribution of tasks among independent individuals, teams, or locations without further “friction losses.”

This article explains the different interface design patterns you should be familiar with and how they can be implemented in the C and C++ programming languages. The first part introduces interface concepts and different interface types.

Interface concept and design variations

A software interface represents a sum of functions with complete semantics (name, parameters, parameter types, return types, special modifiers) for access and implementation.

At least one element (accessor) accesses the interface. The accessor expects the interface (required interface).

At least one element (realization) must implement the interface. The implementation provides the interface (provided interface).

The interface serves to decouple the accessor from the implementation and, at a higher level, to decouple architectural elements.

Interface concept

Figure 1:Interface concept

In different design variations between accessor and interface(s), ...

  • one accessor accesses one or more interfaces
  • multiple accessors access one  ; one interface
  • multiple accessors access one or several interfaces
  • multiple accessors access different interface levels or
  • multiple accessors each access different interface levels  ;  ;

The following design variants are possible for interface implementation:

  • one interface with a single implementation
  • one interface with multiple or partial implementations
  • multiple interfaces in one or more implementation variants

Interface typing

Interfaces can be categorized by topic (depending on the various callers); for example, one interface per architecture element for configuration, diagnostics, and normal operation. In general, interfaces can be categorized into three different types:

  • The call interface, for example, provides the caller from architectural element A with functions to read values from architectural element B, write values to it, or trigger algorithms there.
  • The callback interface actively notifies architecture element A of new values or events from architecture element B. Regarding the structure, it should be noted that the implementation of the callback interface extends across architecture element boundaries and is located here in architecture element A. In conjunction with the call interface, this still results in a unidirectional dependency between the two architectural elements A and B.
  • If the callback is registered dynamically at runtime rather than statically at compile time, a special element (manager) in the software architecture is responsible for this. This manager can register and unregister one or more elements that implement callbacks using a special callback registration interface.    

The Observer pattern can be used to implement the structure shown in Figure 2.

The concrete subject receives a new value. By calling the appropriate function from the observer, the concrete subject notifies all previously registered concrete observers of the new value. The observer pattern has since become a very popular pattern for embedded software.

 Callback structure with registration interface

Figure 2: Callback structure with registration interface

The semantic interface and the functions

Interface typing is reflected in the assigned <<Stereotypes>> and in the interface names, which use the prefixes ic (interface), icb (interface callback) and icbreg (interface callback registration). As interface name , a descriptive and meaningful noun is suitable.

In all interface types, the functions are public .

For functions, programming-language-specific modifiers such as virtual, const, static, inline, etc. must be taken into account.

As function names, verb/noun combinations are suitable. For callback interface functions, the verb "notify" is appropriate. For callback registration interface functions, the verbs "register" and "unregister" are appropriate.

Function parameters are optional and should not exceed a maximum of seven to twelve. The direction can be indicated by “in,” “out,” or “inout” before the parameter name. If the programming language (e.g., C++) supports a default parameter value, this can be specified.

The order of the function parameters can affect performance. You should start with the standard data types, since the compiler can pass these into the function via CPU registers (provided there are still free registers). As soon as a complex data type, such as a structure, is passed by value, the compiler passes this and all subsequent parameters into the function via the stack.

As Parameter name a noun or a combination of several meaningful and expressive nouns is suitable.

Parameterand return types may optionally be `void`. The data types used can be either standard data types from the programming language or custom data types that have already been defined; however, they must not be data types that will be defined in the future.

For performance reasons, passing by pointer/reference (not a copy!) is always preferable to passing by value (a copy!).

Data type names should be identified by a postfix _t.

As with functions, types also have programming language-specific modifiers such as const, static, *, &,[], … .

Approaches to interface implementation

In the simplest case, an interface is a header file containing a set of declared functions. The caller includes the interface header file to call the interface functions. One or more implementing modules include the header file to implement the interface functions. This simplest implementation approach will not be discussed further here. Instead, we will focus on more advanced implementation options in C++ and, where appropriate, in C as well.

The interface designs presented at the beginning can be implemented using the following approaches:

Implementation approaches

Figure 3: Implementation approaches

A non-polymorphic structure means that the interface has exactly one implementation, whereas a polymorphic structure refers to the presence of multiple implementations. In polymorphic structures, a distinction can be made between the types of binding between an object or function pointer and a function. Dynamic binding (runtime binding) allows for context-dependent calls to different implementations of interface functions. With static binding (compile-time binding), the compiler already binds an interface function implementation that cannot be changed at runtime.

The different implementation approaches can be evaluated as follows:

Evaluation of the implementation approaches

Figure 4: Evaluation of the implementation approaches

Choosing the right approach depends heavily on the software quality requirements that need to be met. If a high degree of flexibility at runtime is required, polymorphic structures with dynamic binding are the right choice. If functional safety takes precedence over flexibility, non-polymorphic structures or polymorphic structures with static binding are preferable.

The second part of this article highlights implementation approaches using association, composition, facades, virtual interfaces, non-virtual interfaces, C++ templates, and the CRTPpattern.

Gain the knowledge you need to understand the different options available for interface design and how they can be implemented in the C and C++ programming languages.

MicroConsult offers you professional training and coaching on the topics Analysis, design and architecture and many more.

Further information

Related trainings:

MicroConsult training & coaching overview

MicroConsult Expertise: Embedded Software Engineering

MicroConsult Newsletter

With the MicroConsult newsletter, you'll stay on the pulse of the embedded world. Look forward to proven practical knowledge, real professional tips, and current events – directly from our experts for your project success.

Subscribe now!

Published by

Thomas Batt

Thomas Batt

Thomas Batt has been a certified Trainer and Coach for embedded systems engineering and process consulting for more than 20 years. His main focus is on embedded systems/software engineering and management as well as project, process and corporate coaching. Before starting his career as trainer, he was an engineer for power electronics, medical electronics, CompactPCI/VME bus and peripheral boards.