Antes de la POO, ¿se dejaron públicos los miembros de la estructura de datos?

45

Cuando una estructura de datos (por ejemplo, una cola) se implementa usando un lenguaje OOP, algunos miembros de la estructura de datos deben ser privados (por ejemplo, la cantidad de elementos en la cola).

Una cola también se puede implementar en un lenguaje de procedimiento utilizando un struct y un conjunto de funciones que operan en el struct . Sin embargo, en un lenguaje de procedimiento no puede hacer que los miembros de struct sean privados. ¿Se dejaron públicos los miembros de una estructura de datos implementada en un lenguaje de procedimiento o hubo algún truco para hacerlos privados?

    
pregunta Christopher 18.07.2018 - 22:36

8 respuestas

141

OOP no inventó la encapsulación y no es sinónimo de encapsulación. Muchos lenguajes OOP no tienen modificadores de acceso de estilo C ++ / Java. Muchos idiomas que no son OOP tienen varias técnicas disponibles para ofrecer encapsulación.

Un enfoque clásico para la encapsulación es cierres , como se usa en programación funcional . Esto es significativamente más antiguo que el OOP, pero es de una manera equivalente. P.ej. en JavaScript podríamos crear un objeto como este:

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

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

El objeto plus2 anterior no tiene ningún miembro que permita el acceso directo a x ; está completamente encapsulado. El método add() es un cierre sobre la variable x .

El lenguaje C admite algunos tipos de encapsulación a través de su archivo de encabezado , particularmente la técnica de puntero opaco . En C, es posible declarar un nombre de estructura sin definir sus miembros. En ese momento no se puede usar ninguna variable del tipo de esa estructura, pero podemos usar los punteros a esa estructura libremente (porque el tamaño de un puntero de estructura se conoce en el momento de la compilación). Por ejemplo, considere este archivo de encabezado:

#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

Ahora podemos escribir código que use esta interfaz de Adder, sin tener acceso a sus campos, por ejemplo:

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

Y aquí están los detalles de la implementación totalmente encapsulados:

#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;
}

También existe la clase de lenguajes de programación modular , que se centra en el nivel de módulo interfaces La familia del lenguaje ML incl. OCaml incluye un enfoque interesante para los módulos llamados functors . La programación orientada a objetos (OOP, por sus siglas en inglés) ha ensombrecido y en gran parte la programación modular, aunque muchas de las supuestas ventajas de la POO se refieren más a la modularidad que a la orientación a objetos.

También se observa que las clases en lenguajes OOP como C ++ o Java a menudo no se usan para objetos (en el sentido de entidades que resuelven operaciones a través del enlace tardío / envío dinámico) sino simplemente para tipos de datos abstractos (donde definimos una interfaz pública que oculta los detalles de la implementación interna). El documento Sobre la comprensión de la abstracción de datos, revisado (Cook, 2009) analiza esta diferencia con más detalle.

Pero sí, muchos idiomas no tienen ningún mecanismo de encapsulación. En estos idiomas, los miembros de la estructura se dejan públicos. A lo sumo, habría una convención de nomenclatura desalentadora de uso. P.ej. Creo que Pascal no tenía un mecanismo de encapsulación útil.

    
respondido por el amon 18.07.2018 - 23:16
31

Primero, ser orientado a objetos y orientado a objetos no tiene nada que ver con público contra privado. Muchos lenguajes orientados a objetos no tienen noción de control de acceso.

En segundo lugar, en "C", que la mayoría de las personas denominaría de procedimiento y no orientada a objetos, hay muchos trucos que puedes usar para hacer las cosas de manera efectiva. Una muy común es usar punteros opacos (por ejemplo, nulos *). O bien, puede reenviar declarar un objeto y simplemente no definirlo en un archivo de encabezado.

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) { ... }

¡Mira el SDK de Windows! Utiliza HANDLE y UINT_PTR, y cosas como eso son controladores genéricos de la memoria utilizada en las API, lo que hace que las implementaciones sean privadas.

    
respondido por el Lewis Pringle 18.07.2018 - 23:14
13

"Tipos de datos opacos" era un concepto bien conocido cuando hice mi licenciatura en ciencias de la computación hace 30 años. No cubrimos la POO, ya que no era de uso común en ese momento y se consideró que la "programación funcional" era más correcta.

Modula-2 tenía soporte directo para ellos, consulte enlace .

Lewis Pringle ya ha explicado cómo se puede usar la declaración de avance de la estructura en C. A diferencia del Módulo 2, se tuvo que proporcionar una función de fábrica para crear el objeto. ( Los métodos virtuales también eran fáciles de implementar en C porque el primer miembro de una estructura es un indicador de otra estructura que contenía punteros de función a los métodos.)

A menudo también se utilizó la convención. Por ejemplo, no se debe acceder a ningún campo que comience por "_" fuera del archivo que poseía los datos. Esto se impuso fácilmente mediante la creación de herramientas de comprobación personalizadas.

Todos los proyectos a gran escala en los que he trabajado (antes de pasar a C ++ y luego a C #) tenían un sistema implementado para evitar que los datos "privados" fueran accedidos por el código incorrecto. Fue un poco menos estandarizado de lo que es ahora.

    
respondido por el Ian 19.07.2018 - 13:55
9

Tenga en cuenta que hay muchos idiomas OO sin una capacidad incorporada para marcar miembros como privados. Esto se puede hacer por convención, sin necesidad de que el compilador haga cumplir la privacidad. Por ejemplo, las personas a menudo prefijan las variables privadas con un guión bajo.

Existen técnicas para dificultar el acceso a las variables "privadas", la más común es el lenguaje PIMPL . Esto coloca sus variables privadas en una estructura separada, con solo un puntero asignado en sus archivos de encabezado públicos. Esto significa una falta de referencia adicional y un reparto para obtener cualquier variable privada, algo como ((private_impl)(obj->private))->actual_value , lo que se vuelve molesto, por lo que en la práctica rara vez se usa.

    
respondido por el Karl Bielefeldt 18.07.2018 - 23:06
4

Las estructuras de datos no tenían "miembros", solo campos de datos (asumiendo que era un tipo de registro). La visibilidad se establece normalmente para todo el tipo. Sin embargo, puede que no sea tan limitante como crees, porque las funciones no formaban parte del registro.

Regresemos y obtengamos un poco de historia aquí ...

El paradigma de programación dominante antes de OOP se llamó programación estructurada . El objetivo principal inicial de esto fue evitar el uso de instrucciones de salto no estructuradas ("goto" s). Este es un paradigma orientado al flujo de control (mientras que la POO está más orientada a los datos), pero todavía era una extensión natural de este para tratar de mantener los datos estructurados lógicamente como el código.

Otra rama de la programación estructurada fue ocultación de información , la idea de que las implementaciones de la estructura del código (que es probable cambiar con bastante frecuencia) debe mantenerse separado de la interfaz (lo que idealmente no cambiará tanto). Ahora es un dogma, pero en la antigüedad, mucha gente consideraba mejor que todos los desarrolladores conocieran los detalles de todo el sistema, por lo que en un momento esto fue en realidad una idea controvertida. La edición original de Brook The Mythical Man Month argumentó en contra de la ocultación de la información.

Los lenguajes de programación posteriores diseñados explícitamente para ser buenos Los lenguajes de programación estructurada (por ejemplo, Modula-2 y Ada) generalmente incluían la ocultación de información como un concepto fundamental, construido en torno a algún tipo de concepto de una facilidad de funciones cohesiva (y cualquier tipo, constantes, y objetos que pudieran requerir). En Modula-2 estos fueron llamados "Módulos", en Ada "Paquetes". Muchos lenguajes OOP modernos llaman al mismo concepto "espacios de nombres". Estos espacios de nombres fueron la base organizativa del desarrollo en estos idiomas, y para la mayoría de los propósitos se podrían usar de manera similar a las clases de POO (sin apoyo real para la herencia, por supuesto).

Entonces, en Modula-2 y Ada (83) podría declarar cualquier rutina, tipo, constante u objeto en un espacio de nombres privado o público, pero si tenía un tipo de registro, no había (fácil) manera de declarar algunos grabar campos públicos y otros privados. O todo tu registro es público, o no lo es.

    
respondido por el T.E.D. 19.07.2018 - 20:34
0

En C, ya se podrían pasar los punteros a tipos declarados pero no definidos, como han dicho otros, restringiendo el acceso a todos los campos.

También puede tener funciones privadas y públicas sobre una base de módulo a módulo. Las funciones declaradas estáticas en el archivo fuente no son visibles para el exterior, incluso si intenta adivinar su nombre. De manera similar, puede tener variables globales estáticas a nivel de archivo, lo que generalmente es una mala práctica pero permite el aislamiento en base a un módulo.

Probablemente sea importante enfatizar que la restricción de acceso como una convención bien estandarizada en lugar de como una construcción aplicada por el lenguaje funciona bien (ver Python). Además de eso, restringir el acceso a los campos de objetos solo protegerá al programador cuando sea necesario cambiar el valor de los datos dentro de un objeto después de la creación. Que ya es un olor de código. Podría decirse que la palabra clave const de C y en particular de C ++ para los métodos y argumentos de función es una ayuda mucho mayor para el programador que el final bastante pobre de Java.

    
respondido por el Kafein 20.07.2018 - 11:44
0

Si su definición de Público es la capacidad de acceder a la implementación y los datos / propiedades a través de su propio código en cualquier momento, la respuesta es simple: Sí . Sin embargo, se resumió por diversos medios, dependiendo del idioma.

Espero que esto haya respondido sucintamente a tu pregunta.

    
respondido por el RobMac 20.07.2018 - 16:01
-1

Aquí hay un contraejemplo muy simple: en Java, interface s define objetos, pero class es no. Un class define un tipo de datos abstracto, no un objeto.

Ergo, cada vez que usa private en class en Java, tiene un ejemplo de una estructura de datos con miembros privados que no está orientada a objetos.

    
respondido por el Jörg W Mittag 19.07.2018 - 04:47

Lea otras preguntas en las etiquetas