I know SOLID principles were written for object oriented languages.
I found in the book: "Test driven development for embedded C" by Robert Martin, the following sentence in the last chapter of the book:
"Applying the Open-Closed Principle and the Liskov Substitution Principle makes more flexible designs."
As this is a book of C (no c++ or c#), there should be a way of way of implementing this principles.
There exists any standard way for implementing this principles in C?
Open-closed principle states that a system should be designed so that it's open to extension while keeping it closed from modification, or that it can be used and extended without modifying it. An I/O subsystem, as mentioned by Dennis, is a fairly common example: in a reusable system the user should be able to specify how data is read and written instead of assuming that data can only be written to files for example.
The way to implement this depends on your needs: You may allow the user to pass in an open file descriptor or handle, which already enables the use of sockets or pipes in addition to files. Or you may allow the user to pass in pointers to the functions that should be used for reading and writing: this way your system could be used with encrypted or compressed data streams in addition to what the OS permits.
The Liskov substitution principle states that it should always be possible to replace a type with a subtype. In C you don't often have subtypes, but you can apply the principle in the module level: code should be designed so that using an extended version of a module, like a newer version, should not break it. An extended version of a module may use a struct
that has more fields than the original, more fields in an enum
, and similar things, so your code shouldn't assume that a struct that is passed in has a certain size, or that enum values have a certain maximum.
One example of this is how socket addresses are implemented in the BSD socket API: there an "abstract" socket type struct sockaddr
that can stand for any socket address type, and a concrete socket type for each implementation, such as struct sockaddr_un
for Unix domain sockets and struct sockaddr_in
for IP sockets. Functions that work on socket addresses have to be passed a pointer to the data and the size of the concrete address type.