Prima di OOP, i membri della struttura dati erano rimasti pubblici?

45

Quando una struttura dati (ad esempio una coda) viene implementata utilizzando un linguaggio OOP, alcuni membri della struttura dati devono essere privati (ad esempio, il numero di elementi nella coda).

Una coda può anche essere implementata in un linguaggio procedurale utilizzando struct e un insieme di funzioni che operano su struct . Tuttavia, in un linguaggio procedurale non è possibile rendere privati i membri di struct . I membri di una struttura dati implementati in un linguaggio procedurale sono stati lasciati pubblici o c'era qualche trucco per renderli privati?

    
posta Christopher 18.07.2018 - 22:36
fonte

8 risposte

141

OOP non ha inventato l'incapsulamento e non è sinonimo di incapsulamento. Molti linguaggi OOP non hanno modificatori di accesso in stile C ++ / Java. Molte lingue non OOP hanno varie tecniche disponibili per offrire l'incapsulamento.

Un approccio classico per l'incapsulamento è chiusure , come usato nella programmazione funzionale . Questo è significativamente più vecchio di OOP ma è in un modo equivalente. Per esempio. in JavaScript potremmo creare un oggetto come questo:

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

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

L'oggetto plus2 sopra non ha un membro che consentirebbe l'accesso diretto a x - è interamente incapsulato. Il metodo add() è una chiusura sulla variabile x .

Il linguaggio C supporta alcuni tipi di incapsulamento attraverso il suo meccanismo file header , in particolare la tecnica puntatore opaco . In C, è possibile dichiarare un nome struct senza definire i suoi membri. A quel punto non si può usare alcuna variabile del tipo di quella struttura, ma possiamo usare liberamente i puntatori a quella struttura (poiché la dimensione di un puntatore di una struct è nota al momento della compilazione). Ad esempio, considera questo file di intestazione:

#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

Ora possiamo scrivere il codice che utilizza questa interfaccia di Adder, senza avere accesso ai suoi campi, ad esempio:

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

E qui ci sono i dettagli di implementazione totalmente incapsulati:

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

Esiste anche la classe di linguaggi di programmazione modulari , incentrata sul livello di modulo interfacce. La famiglia linguistica ML incl. OCaml include un approccio interessante ai moduli chiamati functors . OOP ha messo in ombra e in gran parte integrato la programmazione modulare, ma molti presunti vantaggi di OOP sono più legati alla modularità che all'orientamento agli oggetti.

C'è anche l'osservazione che le classi nei linguaggi OOP come C ++ o Java spesso non sono usate per objects (nel senso di entità che risolvono le operazioni attraverso late binding / dynamic dispatch) ma semplicemente per tipi di dati astratti (dove definiamo un'interfaccia pubblica che nasconde i dettagli di implementazione interna). Il documento In Understanding Data Abstraction, Revisited (Cook, 2009) discute questa differenza in maggior dettaglio.

Ma sì, molte lingue non hanno alcun meccanismo di incapsulamento. In queste lingue, i membri della struttura sono lasciati pubblici. Al massimo, ci sarebbe una convenzione di denominazione che scoraggia l'uso. Per esempio. Penso che Pascal non abbia un utile meccanismo di incapsulamento.

    
risposta data 18.07.2018 - 23:16
fonte
31

In primo luogo, essere procedurali rispetto a oggetti orientati non ha nulla a che fare con pubblico vs privato. Un sacco di linguaggi orientati agli oggetti non hanno alcuna nozione di controllo degli accessi.

In secondo luogo, in "C" - che la maggior parte delle persone chiamerebbe procedurale, e non orientato agli oggetti, ci sono molti trucchi che puoi usare per rendere le cose effettivamente private. Uno molto comune è usare puntatori opachi (ad esempio void *). Oppure puoi inoltrare un oggetto e non definirlo in un file di intestazione.

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

Guarda l'SDK di Windows! Utilizza HANDLE e UINT_PTR, e cose del genere sono maniglie generiche per la memoria utilizzata nelle API, rendendo effettivamente private le implementazioni.

    
risposta data 18.07.2018 - 23:14
fonte
13

"I tipi di dati opachi" era un concetto ben noto quando ho fatto il mio corso di laurea in informatica 30 anni fa. Non abbiamo coperto OOP poiché non era di uso comune al momento e la "programmazione funzionale" era considerata più corretta.

Modula-2 aveva un supporto diretto per loro, vedi link .

È già stato spiegato da Lewis Pringle in che modo dichiarare che una struttura può essere usata in C. A differenza del Modulo-2, una funzione di fabbrica doveva essere fornita per creare l'oggetto. ( I metodi virtuali erano anche facili da implementare in C avendo il primo membro di una struct come puntatore a un'altra struttura che conteneva i puntatori di funzioni ai metodi.)

Spesso si usava anche la convenzione. Ad esempio, non è possibile accedere a tutti i campi che iniziano con "_" al di fuori del file di proprietà dei dati. Ciò è stato facilmente applicato dalla creazione di strumenti di controllo personalizzati.

Ogni progetto su larga scala su cui ho lavorato (prima di passare a C ++ e C #) disponeva di un sistema per impedire l'accesso ai dati "privati" dal codice sbagliato. Era solo un po 'meno standardizzato di adesso.

    
risposta data 19.07.2018 - 13:55
fonte
9

Nota ci sono molte lingue OO senza una capacità incorporata per contrassegnare i membri come privati. Questo può essere fatto per convenzione, senza la necessità che il compilatore applichi la privacy. Ad esempio, le persone spesso prefisso variabili private con un carattere di sottolineatura.

Esistono tecniche per rendere più difficile l'accesso alle variabili "private", il più comune è l' idioma PIMPL . Questo mette le tue variabili private in una struttura separata, con solo un puntatore allocato nei tuoi file di intestazione pubblici. Questo significa un extra dereferenziamento e un cast per ottenere variabili private, qualcosa come ((private_impl)(obj->private))->actual_value , che diventa fastidioso, quindi in pratica è usato raramente.

    
risposta data 18.07.2018 - 23:06
fonte
4

Le strutture dati non avevano "membri", solo campi dati (supponendo che fosse un tipo di record). La visibilità era tipicamente impostata per l'intero tipo. Tuttavia, potrebbe non essere così limitante come credi, perché le funzioni non facevano parte del record.

Torniamo indietro e prendiamo un po 'di storia qui ...

Il paradigma di programmazione dominante prima di OOP è stato chiamato programmazione strutturata . L'obiettivo principale iniziale di questo era evitare l'uso di istruzioni di salto non strutturate ("goto" s). Questo è un paradigma orientato al flusso di controllo (mentre OOP è più orientato ai dati), ma era ancora una sua naturale estensione per tentare di mantenere i dati logicamente strutturati proprio come il codice.

Un'altra estensione della programmazione strutturata era nascondere le informazioni , l'idea che le implementazioni della struttura del codice (che è probabile cambiare abbastanza spesso) dovrebbe essere tenuto separato dall'interfaccia (che idealmente non cambierà quasi altrettanto). È dogma adesso, ma in passato molte persone in realtà consideravano migliore per ogni sviluppatore conoscere i dettagli dell'intero sistema, quindi questa volta era in realtà un'idea controversa. L'edizione originale di Brook Mese Mythical Man in realtà ha discusso contro l'occultamento delle informazioni.

I linguaggi di programmazione successivi progettati in modo esplicito per essere buoni linguaggi di programmazione strutturata (ad esempio, Modula-2 e Ada) generalmente includevano l'occultamento delle informazioni come concetto fondamentale, costruito attorno a una sorta di concetto di una struttura coesa di funzioni (e qualsiasi tipo, costanti e oggetti che potrebbero essere richiesti). In Modula-2 questi erano chiamati "Moduli", in Ada "Pacchetti". Molti linguaggi OOP moderni chiamano lo stesso concetto "namespace". Questi spazi dei nomi erano il fondamento organizzativo dello sviluppo in questi linguaggi e, per la maggior parte degli scopi, potevano essere usati in modo simile alle classi OOP (ovviamente senza supporto per l'ereditarietà).

Quindi in Modula-2 e Ada (83) puoi dichiarare qualsiasi routine, tipo, costante o oggetto in uno spazio dei nomi privato o pubblico, ma se avevi un tipo di record, non c'era un modo (facile) per dichiarare registra campi pubblico e altri privati. O il tuo intero record è pubblico o non lo è.

    
risposta data 19.07.2018 - 20:34
fonte
0

In C potresti già passare i puntatori ai tipi dichiarati ma indefiniti come altri hanno detto, limitando in effetti l'accesso a tutti i campi.

Puoi anche avere funzioni private e pubbliche su base modulo-modulo. Le funzioni dichiarate statiche nel file sorgente non sono visibili all'esterno, anche se si tenta di indovinare il loro nome. Allo stesso modo, puoi avere variabili globali a livello di file statico, che in genere sono pratiche sbagliate ma consentono l'isolamento su base modulo.

Probabilmente è importante sottolineare che la restrizione di accesso come una convenzione ben standardizzata piuttosto che un costrutto applicato alla lingua funziona bene (vedi Python). Inoltre, limitare l'accesso ai campi oggetto protegge sempre il programmatore solo quando è necessario modificare il valore dei dati all'interno di un oggetto dopo la sua creazione. Che è già un odore di codice. Probabilmente, la C e in particolare la parola chiave const di C ++ per i metodi e gli argomenti delle funzioni è di gran lunga più utile per il programmatore rispetto al piuttosto povero final di Java.

    
risposta data 20.07.2018 - 11:44
fonte
0

Se la tua definizione di Pubblico è la capacità di accedere all'implementazione e ai dati / proprietà tramite il tuo codice in qualsiasi punto, la risposta è semplicemente: . Tuttavia, è stato astratto con diversi mezzi, a seconda della lingua.

Spero che questo abbia risposto brevemente alla tua domanda.

    
risposta data 20.07.2018 - 16:01
fonte
-1

Ecco un esempio di esempio molto semplice: in Java, interface s definisce gli oggetti, ma class es non lo fa. Un class definisce un tipo di dati astratto, non un oggetto.

Ergo, ogni volta che usi private in class in Java, hai un esempio di una struttura dati con membri privati che non è orientato agli oggetti.

    
risposta data 19.07.2018 - 04:47
fonte

Leggi altre domande sui tag