web-dev-qa-db-fra.com

Avant la POO, les membres de la structure de données étaient-ils rendus publics?

Lorsqu'une structure de données (par exemple, une file d'attente) est implémentée à l'aide d'un langage OOP), certains membres de la structure de données doivent être privés (par exemple, le nombre d'éléments dans la file d'attente).

Une file d'attente peut également être implémentée dans un langage procédural en utilisant un struct et un ensemble de fonctions qui opèrent sur le struct. Cependant, dans un langage procédural, vous ne pouvez pas rendre les membres d'un struct privés. Les membres d'une structure de données implémentée dans un langage procédural ont-ils été rendus publics, ou y avait-il une astuce pour les rendre privés?

45
Christopher

La POO n'a pas inventé l'encapsulation et n'est pas synonyme d'encapsulation. De nombreux langages OOP n'ont pas de modificateurs d'accès de style C++/Java. De nombreux langages non-OOP disposent de diverses techniques pour offrir l'encapsulation.

Une approche classique pour l'encapsulation est les fermetures , telles qu'elles sont utilisées dans la programmation fonctionnelle . Ceci est beaucoup plus ancien que OOP mais est en quelque sorte équivalent. Par exemple, en JavaScript, nous pourrions créer un objet comme celui-ci:

function Adder(x) {
  this.add = function add(y) {
    return x + y;
  }
}

var plus2 = new Adder(2);
plus2.add(7);  //=> 9

L'objet plus2 Ci-dessus n'a pas de membre qui permettrait un accès direct à x - il est entièrement encapsulé. La méthode add() est une fermeture sur la variable x.

La langue [~ # ~] c [~ # ~] prend en charge certains types d'encapsulation via son fichier d'en-tête mécanisme, en particulier la technique de pointeur opaque . En C, il est possible de déclarer un nom de structure sans définir ses membres. À ce stade, aucune variable du type de cette structure ne peut être utilisée, mais nous pouvons utiliser librement des pointeurs vers cette structure (car la taille d'un pointeur de structure est connue au moment de la compilation). Par exemple, considérez ce fichier d'en-tête:

#ifndef ADDER_H
#define ADDER_H

typedef struct AdderImpl *Adder;

Adder Adder_new(int x);
void Adder_free(Adder self);
int Adder_add(Adder self, int y);

#endif

Nous pouvons maintenant écrire du code qui utilise cette interface Adder, sans avoir accès à ses champs, par exemple:

Adder plus2 = Adder_new(2);
if (!plus2) abort();
printf("%d\n", Adder_add(plus2, 7));  /* => 9 */
Adder_free(plus2);

Et voici les détails de mise en œuvre totalement encapsulés:

#include "adder.h"

struct AdderImpl { int x; };

Adder Adder_new(int x) {
  Adder self = malloc(sizeof *self);
  if (!self) return NULL;
  self->x = x;
  return self;
}

void Adder_free(Adder self) {
  free(self);
}

int Adder_add(Adder self, int y) {
  return self->x + y;
}

Il y a aussi la classe de langages de programmation modulaires, qui se concentre sur les interfaces au niveau du module. La famille de langues ML incl. OCaml inclut une approche intéressante des modules appelés functors . OOP programmation modulaire éclipsée et largement englobée, pourtant de nombreux avantages supposés de OOP sont plus sur la modularité que sur l’orientation des objets).

Il y a aussi l'observation que les classes dans les langages OOP comme C++ ou Java ne sont souvent pas utilisées pour objets ( dans le sens d'entités qui résolvent les opérations par liaison tardive/répartition dynamique) mais simplement pour types de données abstraits (où nous définissons une interface publique qui cache les détails de l'implémentation interne). sur la compréhension de l'abstraction des données, revisité (Cook, 2009) discute cette différence plus en détail.

Mais oui, de nombreuses langues n'ont aucun mécanisme d'encapsulation. Dans ces langues, les membres de la structure restent publics. Tout au plus, il y aurait une convention de dénomination décourageant l'utilisation. Par exemple. Je pense que Pascal n'avait pas de mécanisme d'encapsulation utile.

139
amon

Premièrement, être orienté procédure versus objet n'a rien à voir avec public vs privé. De nombreux langages orientés objet n'ont aucune notion de contrôle d'accès.

Deuxièmement, dans "C" - que la plupart des gens appellent procédural et non orienté objet, il existe de nombreuses astuces que vous pouvez utiliser pour rendre les choses privées de manière efficace. Un point très courant consiste à utiliser des pointeurs opaques (par exemple void *). Ou - vous pouvez transmettre déclarer un objet et ne pas le définir dans un fichier d'en-tête.

foo.h:

struct queue;
struct queue* makeQueue();
void add2Queue(struct queue* q, int value);
...

foo.c:

struct queue {
    int* head;
    int* head;
};
struct queue* makeQueue() { .... }
void add2Queue(struct queue* q, int value) { ... }

Regardez le SDK Windows! Il utilise HANDLE et UINT_PTR, et des choses comme ça pour être des descripteurs génériques de la mémoire utilisée dans les API - rendant ainsi les implémentations privées.

31
Lewis Pringle

Les "types de données opaques" étaient un concept bien connu lorsque j'ai obtenu mon diplôme en informatique il y a 30 ans. Nous n'avons pas couvert OOP car il n'était pas courant à l'époque et la "programmation fonctionnelle" était considérée comme plus correcte.

Modula-2 avait un support direct pour eux, voir https://www.modula2.org/reference/modules.php .

Lewis Pringle a déjà expliqué comment la déclaration anticipée d'une structure peut être utilisée en C. Contrairement au module 2, une fonction d'usine devait être fournie pour créer l'objet. ( Les méthodes virtuelles étaient également faciles à implémenter en C en faisant en sorte que le premier membre d'une structure soit un pointeur vers une autre structure contenant des pointeurs de fonction vers les méthodes.)

Souvent, la convention a également été utilisée. Par exemple, aucun champ commençant par "_" ne doit être accessible en dehors du fichier propriétaire des données. Cela a été facilement imposé par la création d'outils de vérification personnalisés.

Chaque projet à grande échelle sur lequel j'ai travaillé (avant de passer au C++ puis au C #) avait un système en place pour empêcher l'accès aux données "privées" par le mauvais code. C'était juste un peu moins standardisé qu'aujourd'hui.

13
Ian

Notez qu'il existe de nombreuses langues OO sans possibilité intégrée de marquer les membres comme privés. Cela peut être fait par convention, sans que le compilateur n'impose la confidentialité. Par exemple, les gens préfèrent souvent préfixer variables privées avec un trait de soulignement.

Il existe des techniques pour rendre plus difficile l'accès aux variables "privées", la plus courante étant idiome PIMPL . Cela place vos variables privées dans une structure distincte, avec juste un pointeur alloué dans vos fichiers d'en-tête publics. Cela signifie une déréférence supplémentaire et un cast pour obtenir toutes les variables privées, quelque chose comme ((private_impl)(obj->private))->actual_value, qui devient ennuyeux, donc en pratique est rarement utilisé.

9
Karl Bielefeldt

Les structures de données n'avaient pas de "membres", seulement des champs de données (en supposant qu'il s'agissait d'un type d'enregistrement). La visibilité était généralement définie pour le type entier. Cependant, cela n'est peut-être pas aussi limitatif que vous le pensez, car les fonctions ne faisaient pas partie du dossier.

Retournons et jetons un peu d'histoire ici ...

Le paradigme de programmation dominant avant OOP était appelé programmation structurée . Le principal objectif initial était d'éviter l'utilisation d'instructions de saut non structurées ("goto" s). Ceci est un paradigme orienté sur le flux de contrôle (tandis que OOP est plus orienté sur les données), mais c'était toujours une extension naturelle de pour tenter de garder les données structurées de manière logique, tout comme le code.

Une autre conséquence de la programmation structurée était masquage des informations , l'idée que les implémentations de la structure du code (qui est susceptible de changer assez souvent) devraient être maintenues séparées de l'interface (qui, idéalement, ne changera pas autant que beaucoup). C'est un dogme maintenant, mais dans les temps anciens, beaucoup de gens considéraient qu'il valait mieux pour chaque développeur de connaître les détails de l'ensemble du système, donc c'était à un moment donné une idée controversée. L'édition originale de Brook's Le mois de l'homme mythique a en fait plaidé contre la dissimulation d'informations.

Les langages de programmation ultérieurs conçus explicitement pour être de bons langages de programmation structurée (par exemple, Modula-2 et Ada) incluaient généralement la dissimulation d'informations comme concept fondamental, construit autour d'une sorte de concept d'une facilité cohérente de fonctions (et de tous types, constantes et objets dont ils pourraient avoir besoin). Dans Modula-2, ils étaient appelés "Modules", dans Ada "Packages". Un grand nombre de langages modernes OOP appellent le même concept "espaces de noms". Ces espaces de noms étaient la base organisationnelle du développement dans ces langages, et pour la plupart des usages pourraient être utilisés de manière similaire à OOP classes (sans réelle prise en charge de l'héritage, bien sûr).

Donc, dans Modula-2 et Ada (83), vous pouvez déclarer n'importe quelle routine, type, constante ou objet dans un espace de noms privé ou public, mais si vous aviez un type d'enregistrement, il n'y avait pas de moyen (facile) de déclarer un enregistrement champs publics et autres privés. Soit tout votre dossier est public, soit il ne l'est pas.

5
T.E.D.

En C, vous pouvez déjà passer des pointeurs vers des types déclarés mais non définis comme d'autres l'ont dit, ce qui restreint en fait l'accès à tous les champs.

Vous pouvez également avoir des fonctions privées et publiques de module à module. Les fonctions déclarées statiques dans le fichier source ne sont pas visibles de l'extérieur, même si vous essayez de deviner leur nom. De même, vous pouvez avoir des variables globales statiques au niveau du fichier, ce qui est généralement une mauvaise pratique mais permet l'isolement sur la base d'un module.

Il est probablement important de souligner que la restriction d'accès en tant que convention bien standardisée plutôt qu'en tant que construction imposée par le langage fonctionne très bien (voir Python). De plus, restreindre l'accès aux champs d'objet ne protégera le programmeur que lorsqu'il sera nécessaire de modifier la valeur des données à l'intérieur d'un objet après sa création. Ce qui est déjà une odeur de code. On peut dire que le mot-clé const de C et en particulier de C++ pour les méthodes et les arguments de fonction aide beaucoup plus le programmeur que le final plutôt pauvre de Java.

0
Kafein

Si votre définition de Public est la capacité d'accéder à l'implémentation et aux données/propriétés via votre propre code à tout moment, la réponse est simplement: Oui. Cependant, il a été résumé par divers moyens - selon la langue.

J'espère que cela a répondu succinctement à votre question.

0
RobMac