I am reading Real-Time UML Workshop for Embedded Systems and I do not understand what they are saying here. I have bolded the parts of the text that I do not fully understand. I am trying to understand this from a C stand point of view.
An interface is a named collection of services. Services come in two
basic flavors. Operations are synchronous services that are invoked,
or called by clients. Event receptions are asynchronous services that
are invoked by sending an asynchronous signal to the object accepting
that event. An interface can contain either or both operations and
event receptions. Both operations and event signals can contain data,
called parameters, passed with the invocation. The set of parameters,
along with the name of the service, is called the signature of the
service. A class that is compliant with an interface provides a
method for every operation and an event reception for every signal
specified in the interface.Interfaces allow you to separate the specification of services that may be called on a class from the implementation of those services. A
class defines methods (for operations) and event receptions (for
signals, specified on the state machine). Both methods and event
receptions include the lines of code that implement the service. An
operation or signal is a specification only, and does not include such
implementation detail.Interfaces may not have implementation (either attributes or methods) and are not directly instantiable. A class is said to realize
an interface if it provides a method for every operation specified in
the interface, and those methods have the same names, parameters,
return values, preconditions, and postconditions of the corresponding
operations in the interface.
What is an example (or two!) of implementation of an interface in C ?
From what it seems like, its just a prototype of a specific functions in the header file.
I removed UML and added interface and object-oriented-design because I think the question turns around this subjects rather than UML
OOP languages do it much better, and even in C there are better ways to do this, but here is a crude example. I’ll show an interface with methods only – the concept of event receptions is more complex, but orthogonal to the one of interfaces, and also beside methods and event receptions there can be other things an interface’s implementation needs to fulfill, and I can’t show them all here(definitely not in C!)
#include<stdio.h>
typedef struct {
/// Prints a key-value pair, where value is an integer
void (*printIntItem)(char key[], int value);
/// Prints a key-value pair, where the value is a string
void (*printStringItem)(char key[], char value[]);
} PrintInterface;
void json_printIntItem(char key[], int value) {
printf("{"%s": %d}n", key, value);
}
void json_printStringItem(char key[], char value[]) {
printf("{"%s": "%s"}n", key, value);
}
PrintInterface PrintJson() {
PrintInterface interface;
interface.printIntItem = json_printIntItem;
interface.printStringItem = json_printStringItem;
return interface;
}
void xml_printIntItem(char key[], int value) {
printf("<%s>%d</%s>n", key, value, key);
}
void xml_printStringItem(char key[], char value[]) {
printf("<%s>%s</%s>n", key, value, key);
}
PrintInterface PrintXml() {
PrintInterface interface;
interface.printIntItem = xml_printIntItem;
interface.printStringItem = xml_printStringItem;
return interface;
}
void printStuff(PrintInterface implementation) {
implementation.printIntItem("one", 1);
implementation.printStringItem("two", "2");
}
int main() {
PrintInterface jsonImpl = PrintJson();
printStuff(jsonImpl);
PrintInterface xmlImpl = PrintXml();
printStuff(xmlImpl);
return 0;
}
PrintInterface
is the interface. It has two methods – printIntItem
and printStringItem
. Their contract can be divided into two parts:
- Explicit contract – can be enforced by the language. That’s the signature, what they accept and what they return. Some languages allow adding more things to enforce in this contract – e.g. which exceptions(errors) may be thrown, how the method can interact with other data etc. In this case all that’s enforced is that the methods accept two arguments – a string and a int/string – and return nothing.
- Implicit contract – the documentation, including what can be understood from the methods names and the names of their arguments. The language can not enforce this, so it’s up to the programmer to respect. It’s almost always a bad idea to ignore that part of the contract – if the author bothers to write something in the documentation it’s usually for a reason, and also users of the interface will expect the contract to be respected.
I’ve also written two implementations – PrintJson
and
PrintXml
. What they do is simply to put functions inside an
PrintInterface
struct. Proper OOP languages/libraries have much more
efficient and convenient ways to do this, but this one is for the sake of
being easy to follow. They both return a value of the same type –
PrintInterface
– and that type can be passed around to users of the interface – like printStuff
.
When you run this program, this will be printed:
{"one": 1}
{"two": "2"}
<one>1</one>
<two>2</two>
First printStuff
is called with the JSON implementation. It uses the API to print the key-value pair "one"->1
and then the key-value pair "two"->"2"
, and thus {"one": 1}
and {"two": "2"}
are printed because it uses a JSON implementation.
Then printStuff
is called with the XML implementation. It uses the API to print the key-value pair "one"->1
and then the key-value pair "two"->"2"
, and thus <one>1</one>
and <two>2</two>
are printed because it now uses an XML implementation.
0
This kind of interface is a concept that doesn’t really work that well outside of an at least minimally object oriented language.
In the Object Oriented sense, the interface will be a class along these lines:
class/interface IEatable {
public mustOverride void eatMe();
}
The idea is that if some other class inherit/implements the interface, the compiler will pitch a fit unless it provides a function eatMe matching that signature. And since you know that’s the case, you can later do something like
IEatable myEatable = some_function_returning_some_eatable_subclass();
myEatable.eatMe();
Without worrying about which kind of eatable you’re dealing with at that second, because the compiler will have made sure that that function is valid on whatever object rolls down the pipe.
Now in C, you don’t have language support for pretty much any of that. You can’t declare variables that then point to slightly different versions of what you requested, and you can’t declare datatypes that have built in functions either. Now you could probably mimic this behavior with structs that contain tables of function pointers, but at that point you’re building an OO subsystem inside of C anyway. That’s the kind of thing that OO languages do under the hood, anyway.
C doesn’t support this type of thing, specifically because it’s an OOP concept with a very specific role. I think that the explanation you got was very poor, especially because it never really mentions why interfaces exist. So I’ll mention it here.
First off, an interface is a lot like a class, except you can’t specify any implementation. Any classes that implement an interface MUST implement ALL the methods in it. That’s the simplest way to put it.
Now, an interface is a way to designate extremely abstract concepts that do not share the same implementation, while still maintaining polymorphism. A 2D geometric object is an example of something that could be an interface. The reason being, they all share the same concept of an area, but the way you’d go about getting the area is dependant on the specific shape. A square is WH, a circle is pir^2, etc… So any class that implemented this interface would be forced to implement its own area method. Another example would be a vehicle, since all vehicles have a similar property: moving. However a car, a boat, and a plane, all move in completely different ways.
Interfaces are often misused though. Many people will use interfaces for something that is far too specific. One example I saw recently was using an interface to represent “freezable foods”. This is an example of misusing an interface. All foods I can think of freeze in the exact same fashion. Their temperature is decreased so as to slow the reproduction of bacteria. Now if it was to be a “freezable object” That’s a good example of an interface, because the implementation of freezing water is not the same as freezing meat, nor are either of them the same as freezing hydrogen (which results in a cool phenomenon known as a super solid)
If the implementation is the same for all instances, it should just go straight into a class and be implemented. Putting it in an interface results in code repetition and is extremely bad design.