OOP has not invented encapsulation
Few days ago, I was watching a talk in which the speaker was presenting the history of the software engineering field of the last fifty years.
At some point he has stated that "OOP introduced (encapsulation)(https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))".
For reference, encapsulation is a technique promoting decouple by abstracting (hiding) the data structures and algorithms behind interfaces.
At the beginning of my career I was working on an operating system for SIM cards, it was mostly C and Java Card (a kind of Java 1.4/1.5).
We had to support processor designs, chipsets, and customer customizations.
In order to achieve that, we relied on C headers separation mechanism, let's say we have an interface which deals with "environment" (whatever it means).
We can have a really simple program:
#include <stdlib.h>
#include "env.h"
int main(void)
{
env_t *env = get_env();
display_env(env);
free(env);
}
env.h is a basic header file which describes the interface:
#ifndef ENV_H_
#define ENV_H_
typedef struct _env_t env_t;
env_t *get_env();
void display_env(env_t *env);
#endif /* !ENV_H_ */
We have one abstract type env_t (struct _env_t) and two functions (get_env
and display_env).
Then we can have two implementations:
#include <stdio.h>
#include <stdlib.h>
#include "env.h"
struct _env_t {
int version;
};
env_t *get_env() {
env_t *env = (env_t *) malloc(sizeof(env_t));
env->version = 42;
return env;
}
void display_env(env_t *env) {
printf("enva: %d\n", env->version);
}
And another more "complex" one:
#include <stdio.h>
#include <stdlib.h>
#include "env.h"
struct _env_t {
int version_major;
int version_minor;
};
env_t *get_env() {
env_t *env = (env_t *) malloc(sizeof(env_t));
env->version_major = 4;
env->version_minor = 2;
return env;
}
void display_env(env_t *env) {
printf("envb: %d.%d\n", env->version_major, env->version_minor);
}
We can compile them
$ cc -c *.c
And finally link them:
$ cc -o a main.o enva.o
$ cc -o b main.o envb.o
Let's see the results:
$ ./a
enva: 42
$ ./b
envb: 4.2
Here we are, compile-time (well, linking-time) encapsulation.
On another hand, we can achieve run-time we can rely on function pointers:
typedef struct _env_t {
void (*display)();
} env_t;
Then we can define behaviors:
void display_enva() {
printf("enva: 42d\n");
}
env_t *get_enva() {
env_t *env = (env_t *) malloc(sizeof(env_t));
env->display = &display_enva;
return env;
}
void display_envb() {
printf("envb: 4.2d\n");
}
env_t *get_envb() {
env_t *env = (env_t *) malloc(sizeof(env_t));
env->display = &display_envb;
return env;
}
Let's use it:
int main(void)
{
env_t *enva = get_enva();
enva->display();
free(enva);
env_t *envb = get_envb();
envb->display();
free(envb);
}enva: 42d
envb: 4.2d
I think encapsulation is quite old, older than OOP at least.