|
I’ve noticed that when embedded software developers create an interface, they often just define function prototypes. While this can define an interface, the resulting code is often tightly coupled to the application and does not obey the dependency inversion principle. You can break the dependency by using function pointers collected in a structure. For example, a digital input / output interface might look like the following:
typedef struct {
void init (DioConfig_t const * const Config);
void write (dioPort_t const port, dioPin_t const pin, dioState_t const state);
dioState_t read (dioPort_t const port, dioPin_t const pin);
} dio_base;
There are 5 advantages to writing your code this way:
-
Encapsulation: The interface provides a clear separation between the implementation details and the public API. By encapsulating the functionality within a struct and exposing only the necessary functions, you can control the access to the underlying implementation and hide complex details from the user.
-
Modularity: The interface allows for modularity by abstracting the underlying implementation. By defining a standardized set of functions, you can easily switch or swap different implementations without affecting the code that uses the interface. This promotes code reusability and simplifies maintenance.
-
Portability: Using an interface in C helps in achieving portability across different platforms and architectures. By relying on a well-defined interface, you can write code that is independent of the underlying hardware or software environment. This makes it easier to adapt the code to various systems without significant modifications.
-
Abstraction: The interface provides a level of abstraction, allowing users to interact with the functionality without needing to understand the internal workings. Users can utilize the provided functions without needing to know the specific implementation details or the complexities involved, resulting in a simplified and more intuitive programming experience.
-
Testability: Interfaces facilitate unit testing and mocking. By defining a clear interface, you can easily create mock implementations for testing purposes. This allows you to isolate and test different components independently, ensuring the correctness of individual units and promoting a more robust and reliable software development process.
So the next time you are tempted to just use a vendor supplied interface, or to write tightly coupled code, use this simple technique to break your dependencies and leverage these five benefits.
|