Wenn eine Datenstruktur (z. B. eine Warteschlange) in einer OOP-Sprache implementiert wird, müssen einige Mitglieder der Datenstruktur privat sein (z. B. die Anzahl der Elemente in der Warteschlange).
Eine Warteschlange kann auch in einer prozeduralen Sprache implementiert werden, indem a struct
und eine Reihe von Funktionen verwendet werden, die auf dem Server ausgeführt werden struct
. In einer prozeduralen Sprache können Sie die Mitglieder jedoch nicht zu struct
Privaten machen. Wurden die Mitglieder einer Datenstruktur, die in einer prozeduralen Sprache implementiert wurde, öffentlich gelassen, oder gab es einen Trick, um sie privat zu machen?
object-oriented
data-structures
history
Christopher
quelle
quelle
Antworten:
OOP hat die Kapselung nicht erfunden und ist nicht gleichbedeutend mit der Kapselung. Viele OOP-Sprachen verfügen nicht über Zugriffsmodifikatoren im C ++ / Java-Stil. Viele Nicht-OOP-Sprachen verfügen über verschiedene Techniken, um die Kapselung zu ermöglichen.
Ein klassischer Ansatz für die Kapselung sind Verschlüsse , wie sie in der funktionalen Programmierung verwendet werden . Dies ist deutlich älter als OOP, aber in gewisser Weise gleichwertig. Beispielsweise können wir in JavaScript ein Objekt wie das folgende erstellen:
Das obige
plus2
Objekt hat kein Mitglied, auf das direkt zugegriffen werden kannx
- es ist vollständig gekapselt. Dieadd()
Methode ist ein Abschluss über diex
Variable.Die C- Sprache unterstützt einige Arten der Kapselung durch ihren Header- Dateimechanismus, insbesondere die opake Zeigertechnik . In C ist es möglich, einen Strukturnamen zu deklarieren, ohne seine Mitglieder zu definieren. Zu diesem Zeitpunkt kann keine Variable des Typs dieser Struktur verwendet werden, aber wir können Zeiger auf diese Struktur frei verwenden (da die Größe eines Strukturzeigers zur Kompilierungszeit bekannt ist). Betrachten Sie zum Beispiel diese Header-Datei:
Wir können jetzt Code schreiben, der diese Adder-Schnittstelle verwendet, ohne Zugriff auf seine Felder zu haben, zB:
Und hier wären die vollständig gekapselten Implementierungsdetails:
Es gibt auch die Klasse der modularen Programmiersprachen , die sich auf Schnittstellen auf Modulebene konzentriert. Die ML-Sprachfamilie inkl. OCaml enthält einen interessanten Ansatz für Module, die als Funktoren bezeichnet werden . OOP hat die modulare Programmierung überschattet und weitestgehend unterbewertet, doch bei vielen angeblichen Vorteilen von OOP geht es mehr um Modularität als um Objektorientierung.
Es gibt auch die Beobachtung, dass Klassen in OOP-Sprachen wie C ++ oder Java oft nicht für Objekte verwendet werden (im Sinne von Entitäten, die Operationen durch späte Bindung / dynamischen Versand auflösen), sondern nur für abstrakte Datentypen (wobei wir eine öffentliche Schnittstelle definieren, die verborgen bleibt) Details zur internen Implementierung). In dem Aufsatz Über das Verständnis der Datenabstraktion, Revisited (Cook, 2009) wird dieser Unterschied ausführlicher erörtert.
Aber ja, viele Sprachen haben überhaupt keinen Verkapselungsmechanismus. In diesen Sprachen werden Strukturmitglieder öffentlich gelassen. Allenfalls würde eine Namenskonvention von der Verwendung abhalten. Ich glaube, Pascal hatte keinen nützlichen Einkapselungsmechanismus.
quelle
Adder self = malloc(sizeof(Adder));
? Es gibt einen Grund, warum man Zeiger tippt undsizeof(TYPE)
im Allgemeinen verpönt ist.sizeof(*Adder)
, weil*Adder
es kein Typ ist, genauso wie*int *
es kein Typ ist. Der AusdruckT t = malloc(sizeof *t)
ist sowohl idiomatisch als auch korrekt. Siehe meine Bearbeitung.private static
Variablen in Java. Ähnlich wie bei C können Sie auch undurchsichtige Zeiger verwenden, um Daten in Pascal zu übergeben, ohne zu deklarieren, um was es sich handelt. Das klassische MacOS verwendete viele undurchsichtige Zeiger, da öffentliche und private Teile eines Datensatzes (Datenstruktur) möglicherweise zusammen übergeben werden. Ich erinnere mich, dass Window Manager viel davon getan hat, da Teile des Fensterdatensatzes öffentlich waren, aber auch einige interne Informationen enthalten waren._private_member
undoutput_property_
oder fortgeschrittenere Techniken zum Erstellen von unveränderlichen Objekten zurückgreift.Erstens hat prozedural versus objektorientiert nichts mit öffentlich oder privat zu tun. Viele objektorientierte Sprachen kennen keine Zugriffskontrolle.
Zweitens gibt es in "C" - was die meisten Leute als prozedural und nicht objektorientiert bezeichnen würden - viele Tricks, mit denen Sie Dinge effektiv privat machen können. Sehr häufig werden undurchsichtige (z. B. leere *) Zeiger verwendet. Oder - Sie können ein Objekt weiterleiten, ohne es in einer Header-Datei zu definieren.
foo.h:
foo.c:
Schau dir das Windows SDK an! Es verwendet HANDLE und UINT_PTR und solche Dinge sind generische Handles für den in APIs verwendeten Speicher - wodurch die Implementierungen effektiv privat werden.
quelle
"Undurchsichtige Datentypen" war ein bekanntes Konzept, als ich vor 30 Jahren meinen Abschluss in Informatik machte. Wir haben OOP nicht behandelt, da es zu diesem Zeitpunkt nicht gebräuchlich war und "funktionale Programmierung" als korrekter angesehen wurde.
Modula-2 hatte direkte Unterstützung für sie, siehe https://www.modula2.org/reference/modules.php .
Lewis Pringle hat bereits erklärt, wie die Forward-Deklaration einer Struktur in C verwendet werden kann. Im Gegensatz zu Modul 2 musste zum Erstellen des Objekts eine Factory-Funktion bereitgestellt werden. ( Virtuelle Methoden waren auch einfach in C zu implementieren, indem das erste Element einer Struktur ein Zeiger auf eine andere Struktur war, die Funktionszeiger auf die Methoden enthielt.)
Oft wurde auch Konvention verwendet. Beispielsweise sollte auf Felder, die mit "_" beginnen, nicht außerhalb der Datei zugegriffen werden, deren Eigentümer die Daten sind. Dies wurde leicht durch die Erstellung von benutzerdefinierten Überprüfungswerkzeugen durchgesetzt.
Jedes große Projekt, an dem ich gearbeitet habe (bevor ich auf C ++ umgestiegen bin, dann auf C #), hatte ein System, um zu verhindern, dass auf "private" Daten mit dem falschen Code zugegriffen wird. Es war nur ein bisschen weniger standardisiert als jetzt.
quelle
Beachten Sie, dass es viele OO-Sprachen gibt, in denen Mitglieder nicht als privat markiert werden können. Dies kann durch Konvention erfolgen, ohne dass der Compiler die Privatsphäre durchsetzen muss. Beispielsweise wird privaten Variablen häufig ein Unterstrich vorangestellt.
Es gibt Techniken, die den Zugriff auf "private" Variablen erschweren . Am häufigsten wird die PIMPL-Sprache verwendet . Dies versetzt Ihre privaten Variablen in eine separate Struktur, wobei nur ein Zeiger in Ihren öffentlichen Header-Dateien zugewiesen wird. Dies bedeutet eine zusätzliche Dereferenzierung und eine Besetzung, um etwa private Variablen zu erhalten, was
((private_impl)(obj->private))->actual_value
ärgerlich wird, weshalb in der Praxis selten Gebrauch gemacht wird.quelle
Datenstrukturen hatten keine "Mitglieder", nur Datenfelder (vorausgesetzt, es war ein Datensatztyp). Die Sichtbarkeit wurde normalerweise für den gesamten Typ festgelegt. Dies ist jedoch möglicherweise nicht so einschränkend, wie Sie denken, da die Funktionen nicht Teil des Datensatzes waren.
Kommen wir zurück und werfen einen Blick auf die Geschichte ...
Das vorherrschende Programmierparadigma vor OOP wurde als strukturierte Programmierung bezeichnet . Das ursprüngliche Hauptziel war es, die Verwendung von unstrukturierten Sprunganweisungen ("goto" s) zu vermeiden. Dies ist ein Kontrollfluss-orientiertes Paradigma (während OOP mehr datenorientiert ist), aber es war immer noch eine logische Erweiterung davon, um zu versuchen, Daten logisch strukturiert zu halten, genau wie der Code.
Ein weiterer Ausweg aus der strukturierten Programmierung war das Verbergen von Informationen , die Idee, dass die Implementierungen der Codestruktur (die sich wahrscheinlich ziemlich oft ändern) von der Schnittstelle getrennt werden sollten (die sich im Idealfall nicht annähernd so stark ändert). Heutzutage ist es ein Dogma, aber in früheren Zeiten hielten es viele Leute für besser, wenn jeder Entwickler die Details des gesamten Systems kennenlernte. Dies war also zu einer Zeit eine umstrittene Idee. Die Originalausgabe von Brooks The Mythical Man Month sprach sich tatsächlich gegen das Verstecken von Informationen aus.
Spätere Programmiersprachen, die explizit als gute strukturierte Programmiersprachen konzipiert wurden (z. B. Modula-2 und Ada), enthielten im Allgemeinen Informationen, die sich als grundlegendes Konzept verbargen und auf eine Art Konzept einer zusammenhängenden Funktionseinheit (und beliebiger Typen, Konstanten und Objekte, die sie möglicherweise benötigen). In Modula-2 wurden diese "Module" genannt, in Ada "Packages". Viele moderne OOP-Sprachen nennen das gleiche Konzept "Namespaces". Diese Namespaces bildeten die organisatorische Grundlage für die Entwicklung in diesen Sprachen und konnten für die meisten Zwecke ähnlich wie OOP-Klassen verwendet werden (natürlich ohne echte Unterstützung für die Vererbung).
So in Modula-2 und Ada (83) können Sie jede Routine, Typen, konstant erklären, oder ein Objekt in einem Namespace privat oder öffentlich, aber wenn Sie einen Datensatztyp haben, gibt es keinen (leicht) Weg , um etwas Rekord zu erklären Felder Öffentlichkeit und andere privat. Entweder ist Ihre gesamte Aufzeichnung öffentlich oder nicht.
quelle
object.method()
Aufruf ist nur syntaktischer Zucker. Wichtige IMHO - siehe Meyers Prinzip des einheitlichen Zugangs / Referenz - aber immer noch nur syntaktischer Zucker.object.method()
eine alternative Formmethod(object, ...)
zugestanden haben.In C könnten Sie bereits Zeiger auf deklarierte, aber undefinierte Typen übergeben, wie andere bereits gesagt haben, wodurch der Zugriff auf alle Felder eingeschränkt wird.
Sie können auch private und öffentliche Funktionen von Modul zu Modul ausführen. In der Quelldatei als statisch deklarierte Funktionen sind von außen nicht sichtbar, auch wenn Sie versuchen, ihren Namen zu erraten. In ähnlicher Weise können Sie globale Variablen auf statischer Dateiebene verwenden, was im Allgemeinen keine gute Praxis ist, aber die Isolation auf Modulbasis ermöglicht.
Es ist wahrscheinlich wichtig zu betonen, dass Zugriffsbeschränkungen als eine gut standardisierte Konvention und nicht als ein durch Sprache erzwungenes Konstrukt gut funktionieren (siehe Python). Darüber hinaus schützt die Einschränkung des Zugriffs auf Objektfelder den Programmierer immer dann, wenn der Wert der Daten in einem Objekt nach der Erstellung geändert werden muss. Welches ist schon ein Code-Geruch. Das
const
Schlüsselwort von C und insbesondere von C ++ für Methoden und Funktionsargumente ist für den Programmierer vermutlich eine weitaus größere Hilfe als das eher schlechte von Javafinal
.quelle
static
globale Daten und Operationen (was bedeutete, dass sie dem Linker nicht zur Verwendung durch andere Zusammenstellungen präsentiert wurden). Sie können plausibel argumentieren, dass jede Unterstützung, die C für gute Software-Design-Praktiken hatte, abgesehen davon ein Hack war und nicht Teil des ursprünglichen Designs der Sprache aus dem Jahr 1972.Wenn Ihre Definition von Öffentlich die Möglichkeit ist, jederzeit über Ihren eigenen Code auf Implementierung und Daten / Eigenschaften zuzugreifen, lautet die Antwort einfach: Ja . Sie wurde jedoch - je nach Sprache - auf unterschiedliche Weise abstrahiert.
Ich hoffe, das hat Ihre Frage kurz und bündig beantwortet.
quelle
Hier ist ein sehr einfaches Gegenbeispiel: In Java
interface
definieren s Objekte, aberclass
es nicht. Aclass
definiert einen abstrakten Datentyp, kein Objekt.Ergo, zu jeder Zeit Sie verwenden
private
in einemclass
in Java, haben Sie ein Beispiel für eine Datenstruktur mit privaten Mitgliedern , die nicht objektorientiert ist.quelle