How do you use inheritance in OBC?
The simple answer is: you don't. Inheritance is a source
of trouble in object oriented programming. It breaks encapsulation
(protected attributes), causes dependencies and side effects,
causes big object interfaces ( have a look at how many methods some
Swing objects have) and undermines the Liskov
substitution principle.
So what is the alternative? Delegation. If an object can't handle a message,
it delegates it to another object. Here is an example of a tuned car which
accelerates twice as much as a normal car. The tuned car object forwards
all messages it doesn't know to an internal car object.
tunedcar.h
#ifndef TUNEDCAR_H_
#define TUNEDCAR_H_
Obj tunedcar_create();
#endif /*TUNEDCAR_H_*/
tunedcar.c
#include <stdio.h>
#include <stdlib.h>
#include "../obc/objI.h"
#include "car.h"
#include "tunedcar.h"
#include "vehicleI.h"
static void tunedcar_print(Obj self, FILE* out) {
float speed = vehicle_getSpeed(obj_getForward(self));
fprintf(out, "Tuned car speeds at %.0f km/h\n", speed);
}
static void tunedcar_accelerate(Obj self) {
vehicle_accelerate(obj_getForward(self));
vehicle_accelerate(obj_getForward(self));
}
static void tunedcar_destroy(Car self) {
obj_destroy(obj_getForward(self));
obj_destroyInfo(self);
free(self);
}
Obj tunedcar_create() {
Obj self = malloc(1);
REGISTER(self, obj_print, tunedcar_print);
REGISTER(self, obj_destroy, tunedcar_destroy);
REGISTER(self, vehicle_accelerate, tunedcar_accelerate);
obj_setForward(self, car_create());
return self;
}
Tuned car is a pure OBC object. It can only be used with virtual OBC methods.
This is why the only public method is the constructor method. All other methods
are declared private by using the static key word. Actually tunedcar is an
implementation of the decorator pattern. In a similar way would you for example
implement a proxy pattern.
Delegation is very powerful. If you use it to simulate inheritance, you can
for example change your ancestors at runtime, or have different instances from
the same object type to inherit from different parents.
Here is how we can use tuned cars in the main function:
main.c
#include "../obc/objI.h"
#include "vehicleI.h"
#include "car.h"
#include "tunedcar.h"
#include "boat.h"
int main(){
Obj vehicles[3];
int i;
/* create objects */
vehicles[0] = car_create();
vehicles[1] = tunedcar_create();
vehicles[2] = boat_create();
/* use objects */
for(i=0; i < 3; i++) {
vehicle_accelerate(vehicles[i]);
obj_print(vehicles[i], stdout);
}
/* free objects */
for(i=0; i < 3; i++) {
obj_destroy(vehicles[i]);
}
return 0;
}
If you compile, link and run everything you should get an output like:
Car speeds at 100 km/h
Tuned car speeds at 200 km/h
Boat speeds at 10 km/h
If you want to use delegation to mimic single inheritance, you can either do it explicit or the macro OBJ_ALLOC() can help you. Here is an example how it can be used. To compile this example, include all OBC files in the build.
inheritancemain.c
#include "../obc/obc.h"
typedef struct ParentImpl* Parent;
typedef struct ParentImpl {
double d;
} ParentImpl;
Parent parent_create(double d) {
Parent self = OBJ_ALLOC(ParentImpl, NULL);
self->d = d;
return self;
}
typedef struct ChildImpl* Child;
typedef struct ChildImpl {
int i;
} ChildImpl;
Child child_create(int i, double d) {
Child self = OBJ_ALLOC(ChildImpl, parent_create(d));
self->i = i;
return self;
}
int main(){
Child childA = child_create(1, 2.0);
Child childB = child_create(2, 1.0);
if( obj_compare(childA, childB) != 0) {
obj_print(childA, stdout);
printf(" is not equal to ");
obj_print(childB, stdout);
}
obj_destroy(childA);
obj_destroy(childB);
return 0;
}
Objects created with OBJ_ALLOC() automatically handle the parent object.
They also have some predefined methods:
obj_destroy, obj_compare, obj_copy, obj_clone, obj_print,
obj_getHashCode
They work on
a shallow data base, so if child or parent contain pointer references
to other objects, you must override them. If you need to access the
parent explicitly in a child method, use obj_getForward(child) to
get the parent object.