Ich meine, abgesehen von seinem verbindlichen Namen (der Standard Template Library) ...
C ++ wollte ursprünglich OOP-Konzepte in C präsentieren. Das heißt: Sie können anhand ihrer Klasse und Klassenhierarchie feststellen, was eine bestimmte Entität tun kann und was nicht (unabhängig davon, wie sie es tut). Einige Kompositionen von Fähigkeiten sind auf diese Weise aufgrund der Problematik der Mehrfachvererbung und der Tatsache, dass C ++ das Konzept von Schnittstellen auf etwas ungeschickte Weise unterstützt (im Vergleich zu Java usw.), schwieriger zu beschreiben, aber es ist da (und könnte es sein) verbessert).
Und dann kamen Vorlagen zusammen mit der STL ins Spiel. Die STL schien die klassischen OOP-Konzepte zu übernehmen und sie stattdessen mit Vorlagen in den Abfluss zu spülen.
Es sollte unterschieden werden zwischen Fällen, in denen Vorlagen zur Verallgemeinerung von Typen verwendet werden, bei denen die Typen selbst für den Betrieb der Vorlage irrelevant sind (z. B. Container). Ein vector<int>
macht Sinn.
In vielen anderen Fällen (Iteratoren und Algorithmen) sollten Template-Typen jedoch einem "Konzept" (Eingabe-Iterator, Vorwärts-Iterator usw.) folgen, bei dem die tatsächlichen Details des Konzepts vollständig durch die Implementierung der Vorlage definiert werden Funktion / Klasse und nicht von der Klasse des Typs, der mit der Vorlage verwendet wird, was eine etwas gegen die Verwendung von OOP ist.
Zum Beispiel können Sie die Funktion mitteilen:
void MyFunc(ForwardIterator<...> *I);
Update: Da es in der ursprünglichen Frage unklar war, kann ForwardIterator selbst als Vorlage verwendet werden, um jeden ForwardIterator-Typ zuzulassen. Das Gegenteil ist ForwardIterator als Konzept.
erwartet einen Forward Iterator nur anhand seiner Definition, wobei Sie entweder die Implementierung oder die Dokumentation überprüfen müssen, um:
template <typename Type> void MyFunc(Type *I);
Zwei Behauptungen, die ich für die Verwendung von Vorlagen aufstellen kann: Kompilierter Code kann effizienter gestaltet werden, indem die Vorlage für jeden verwendeten Typ maßgeschneidert kompiliert wird, anstatt vtables zu verwenden. Und die Tatsache, dass Vorlagen mit nativen Typen verwendet werden können.
Ich suche jedoch nach einem tieferen Grund, warum ich die klassische OOP zugunsten der Vorlage für die STL aufgeben sollte. (Angenommen, Sie lesen so weit: P)
vector<int>
undvector<char>
gleichzeitig verwendet zu werden. Sie könnten sicher sein, aber Sie könnten zwei beliebige Codeteile gleichzeitig verwenden. Das hat nichts mit Vorlagen, C ++ oder der STL zu tun. Es gibt nichts in der Instanziierungvector<int>
die erfordertvector<char>
Code geladen oder ausgeführt werden.Antworten:
Die kurze Antwort lautet "weil C ++ weitergezogen ist". Ja, in den späten 70ern wollte Stroustrup ein aktualisiertes C mit OOP-Funktionen erstellen, aber das ist schon lange her. Als die Sprache 1998 standardisiert wurde, war sie keine OOP-Sprache mehr. Es war eine Multi-Paradigmen-Sprache. Es hatte sicherlich eine gewisse Unterstützung für OOP-Code, aber es hatte auch eine vollständig überlagerte Vorlagensprache, es ermöglichte Metaprogrammierung zur Kompilierungszeit und die Leute hatten generische Programmierung entdeckt. Plötzlich schien OOP einfach nicht mehr so wichtig zu sein. Nicht, wenn wir einfacheren, präziseren und effizienteren Code schreiben können, indem wir Techniken verwenden, die über Vorlagen und generische Programmierung verfügbar sind.
OOP ist nicht der heilige Gral. Es ist eine nette Idee, und es war eine ziemliche Verbesserung gegenüber prozeduralen Sprachen in den 70er Jahren, als es erfunden wurde. Aber es ist ehrlich gesagt nicht alles, worauf es ankommt. In vielen Fällen ist es ungeschickt und ausführlich und fördert nicht wirklich wiederverwendbaren Code oder Modularität.
Aus diesem Grund ist die C ++ - Community heute viel mehr an generischer Programmierung interessiert, und alle beginnen endlich zu erkennen, dass funktionale Programmierung auch ziemlich clever ist. OOP alleine ist einfach kein schöner Anblick.
Versuchen Sie, ein Abhängigkeitsdiagramm einer hypothetischen "OOP-ified" STL zu zeichnen. Wie viele Klassen müssten voneinander wissen? Es würde viele Abhängigkeiten geben. Könnten Sie nur den
vector
Header einfügen, ohne ihn auch zu bekommeniterator
oder gariostream
einzuziehen? Die STL macht dies einfach. Ein Vektor kennt den von ihm definierten Iteratortyp und das ist alles. Die STL-Algorithmen wissen nichts . Sie müssen nicht einmal einen Iterator-Header enthalten, obwohl sie alle Iteratoren als Parameter akzeptieren. Was ist dann modularer?Die STL folgt möglicherweise nicht den Regeln von OOP, wie Java sie definiert, aber erreicht sie nicht die Ziele von OOP? Erreicht es nicht Wiederverwendbarkeit, geringe Kopplung, Modularität und Kapselung?
Und erreicht es diese Ziele nicht besser als eine OOP-Version?
Warum die STL in die Sprache übernommen wurde, geschahen mehrere Dinge, die zur STL führten.
Zunächst wurden Vorlagen zu C ++ hinzugefügt. Sie wurden aus dem gleichen Grund hinzugefügt wie Generika zu .NET. Es schien eine gute Idee zu sein, Dinge wie "Container vom Typ T" schreiben zu können, ohne die Sicherheit des Typs wegzuwerfen. Natürlich war die Implementierung, für die sie sich entschieden hatten, viel komplexer und leistungsfähiger.
Dann stellten die Leute fest, dass der von ihnen hinzugefügte Vorlagenmechanismus noch leistungsfähiger war als erwartet. Und jemand fing an, mit Vorlagen zu experimentieren, um eine allgemeinere Bibliothek zu schreiben. Eine, die von der funktionalen Programmierung inspiriert ist und alle neuen Funktionen von C ++ nutzt.
Er präsentierte es dem C ++ - Sprachkomitee, das eine Weile brauchte, um sich daran zu gewöhnen, weil es so seltsam und anders aussah, aber letztendlich erkannte, dass es besser funktionierte als die traditionellen OOP-Äquivalente, die sie sonst einbeziehen müssten . Also nahmen sie einige Anpassungen vor und übernahmen es in die Standardbibliothek.
Es war keine ideologische Entscheidung, es war keine politische Entscheidung, "wollen wir OOP sein oder nicht", sondern eine sehr pragmatische. Sie bewerteten die Bibliothek und stellten fest, dass sie sehr gut funktionierte.
In jedem Fall sind beide Gründe, die Sie für die Bevorzugung der STL nennen, unbedingt erforderlich.
Die C ++ - Standardbibliothek muss effizient sein. Wenn es weniger effizient ist als beispielsweise der entsprechende handgerollte C-Code, wird es nicht verwendet. Das würde die Produktivität senken, die Wahrscheinlichkeit von Fehlern erhöhen und insgesamt nur eine schlechte Idee sein.
Und die STL muss mit primitiven Typen arbeiten, denn primitive Typen sind alles, was Sie in C haben, und sie sind ein Hauptbestandteil beider Sprachen. Wenn die STL nicht mit nativen Arrays funktionieren würde, wäre sie nutzlos .
Ihre Frage geht stark davon aus, dass OOP "am besten" ist. Ich bin gespannt warum. Sie fragen, warum sie "die klassische OOP aufgegeben" haben. Ich frage mich, warum sie dabei bleiben sollten. Welche Vorteile hätte es gehabt?
quelle
std::set
als Beispiel. Es erbt nicht von einer abstrakten Basisklasse. Wie schränkt das Ihre Nutzung von einstd::set
? Gibt es etwas, das Sie mit a nicht tun können,std::set
weil es nicht von einer abstrakten Basisklasse erbt?Die direkteste Antwort auf das, worüber Sie meiner Meinung nach fragen / sich beschweren, lautet: Die Annahme, dass C ++ eine OOP-Sprache ist, ist eine falsche Annahme.
C ++ ist eine Multi-Paradigmen-Sprache. Es kann nach OOP-Prinzipien programmiert werden, es kann prozedural programmiert werden, es kann generisch programmiert werden (Vorlagen) und mit C ++ 11 (früher bekannt als C ++ 0x) können einige Dinge sogar funktional programmiert werden.
Die Entwickler von C ++ sehen dies als Vorteil an, und sie würden argumentieren, dass die Einschränkung von C ++, sich wie eine reine OOP-Sprache zu verhalten, wenn die generische Programmierung das Problem besser löst und allgemeiner ein Rückschritt wäre.
quelle
Meines Wissens nach bevorzugte Stroustrup ursprünglich ein Container-Design im "OOP-Stil" und sah keinen anderen Weg, dies zu tun. Alexander Stepanov ist derjenige, der für die STL verantwortlich ist, und seine Ziele beinhalteten nicht "objektorientiert machen" :
(Er erklärt, warum Vererbung und Virtuals - auch bekannt als objektorientiertes Design - im Rest des Interviews "grundlegend fehlerhaft waren und nicht verwendet werden sollten").
Nachdem Stepanov Stroustrup seine Bibliothek vorgestellt hatte, unternahmen Stroustrup und andere Herkulesbemühungen, um sie in den ISO C ++ - Standard aufzunehmen (gleiches Interview):
quelle
Die Antwort findet sich in diesem Interview mit Stepanov, dem Autor der STL:
quelle
Warum wäre ein reines OOP-Design für eine Datenstruktur- und Algorithmusbibliothek besser ?! OOP ist nicht die Lösung für alles.
IMHO, STL ist die eleganteste Bibliothek, die ich je gesehen habe :)
für Ihre Frage,
Sie benötigen keinen Laufzeitpolymorphismus. Für STL ist es ein Vorteil, die Bibliothek mithilfe statischen Polymorphismus zu implementieren. Dies bedeutet Effizienz. Versuchen Sie, eine generische Sortierung oder Entfernung oder einen beliebigen Algorithmus zu schreiben, der für ALLE Container gilt! Ihre Sortierung in Java würde Funktionen aufrufen, die durch n-Ebenen dynamisch sind, um ausgeführt zu werden!
Sie brauchen dumme Dinge wie Boxen und Unboxen, um böse Annahmen der sogenannten Pure OOP-Sprachen zu verbergen.
Das einzige Problem, das ich mit STL und Vorlagen im Allgemeinen sehe, sind die schrecklichen Fehlermeldungen. Was mit Concepts in C ++ 0X gelöst wird.
Das Vergleichen von STL mit Sammlungen in Java ist wie das Vergleichen von Taj Mahal mit meinem Haus :)
quelle
static_assert
vielleicht vorbelegt werden .Ich denke, Sie verstehen die beabsichtigte Verwendung von Konzepten durch Vorlagen falsch. Forward Iterator zum Beispiel ist ein sehr genau definiertes Konzept. Informationen zu den Ausdrücken, die gültig sein müssen, damit eine Klasse ein Vorwärtsiterator ist, und zu ihrer Semantik einschließlich der Komplexität der Berechnungen finden Sie im Standard oder unter http://www.sgi.com/tech/stl/ForwardIterator.html (Sie müssen den Links zu Input, Output und Trivial Iterator folgen, um alles zu sehen).
Dieses Dokument ist eine perfekte Schnittstelle, und "die tatsächlichen Details des Konzepts" werden genau dort definiert. Sie werden nicht durch die Implementierungen von Vorwärtsiteratoren definiert, und sie werden auch nicht durch die Algorithmen definiert, die Vorwärtsiteratoren verwenden.
Es gibt drei Unterschiede im Umgang mit Schnittstellen zwischen STL und Java:
1) STL definiert gültige Ausdrücke unter Verwendung des Objekts, während Java Methoden definiert, die für das Objekt aufrufbar sein müssen. Natürlich kann ein gültiger Ausdruck ein Methodenaufruf (Elementfunktion) sein, muss es aber nicht sein.
2) Java-Schnittstellen sind Laufzeitobjekte, während STL-Konzepte zur Laufzeit selbst mit RTTI nicht sichtbar sind.
3) Wenn Sie die erforderlichen gültigen Ausdrücke für ein STL-Konzept nicht gültig machen, wird ein nicht angegebener Kompilierungsfehler angezeigt, wenn Sie eine Vorlage mit dem Typ instanziieren. Wenn Sie eine erforderliche Methode einer Java-Schnittstelle nicht implementieren können, wird ein bestimmter Kompilierungsfehler angezeigt.
Dieser dritte Teil ist, wenn Sie eine Art (zur Kompilierungszeit) "Ententypisierung" mögen: Schnittstellen können implizit sein. In Java sind Schnittstellen etwas explizit: Eine Klasse "ist" Iterable genau dann, wenn sie besagt, dass sie Iterable implementiert. Der Compiler kann überprüfen, ob die Signaturen seiner Methoden alle vorhanden und korrekt sind, aber die Semantik ist immer noch implizit (dh sie sind entweder dokumentiert oder nicht, aber nur mehr Code (Komponententests) kann Ihnen sagen, ob die Implementierung korrekt ist).
In C ++ sind wie in Python sowohl Semantik als auch Syntax implizit, obwohl Sie in C ++ (und in Python, wenn Sie den Präprozessor mit starker Typisierung erhalten) Hilfe vom Compiler erhalten. Wenn ein Programmierer eine Java-ähnliche explizite Deklaration von Schnittstellen durch die implementierende Klasse benötigt, besteht der Standardansatz darin, Typmerkmale zu verwenden (und Mehrfachvererbung kann verhindern, dass dies zu ausführlich ist). Was im Vergleich zu Java fehlt, ist eine einzelne Vorlage, die ich mit meinem Typ instanziieren kann und die nur dann kompiliert wird, wenn alle erforderlichen Ausdrücke für meinen Typ gültig sind. Dies würde mir sagen, ob ich alle erforderlichen Bits implementiert habe, "bevor ich es benutze". Das ist eine Annehmlichkeit, aber es ist nicht der Kern von OOP (und es testet immer noch nicht die Semantik,
STL kann für Ihren Geschmack ausreichend OO sein oder auch nicht, aber es trennt die Schnittstelle sicher sauber von der Implementierung. Es fehlt Javas Fähigkeit, über Schnittstellen nachzudenken, und es werden Verstöße gegen die Schnittstellenanforderungen unterschiedlich gemeldet.
Persönlich denke ich, dass implizite Typen eine Stärke sind, wenn sie angemessen verwendet werden. Der Algorithmus sagt, was er mit seinen Vorlagenparametern macht, und der Implementierer stellt sicher, dass diese Dinge funktionieren: Es ist genau der gemeinsame Nenner dessen, was "Schnittstellen" tun sollen. Darüber hinaus ist es unwahrscheinlich, dass Sie mit STL beispielsweise
std::copy
die Forward-Deklaration in einer Header-Datei finden. Programmierer sollten anhand ihrer Dokumentation und nicht nur anhand der Funktionssignatur herausfinden, was eine Funktion benötigt. Dies gilt für C ++, Python oder Java. Es gibt Einschränkungen, was mit dem Tippen in einer beliebigen Sprache erreicht werden kann, und der Versuch, mit dem Tippen etwas zu tun, was nicht funktioniert (Semantik überprüfen), wäre ein Fehler.Allerdings benennen STL-Algorithmen ihre Vorlagenparameter normalerweise so, dass klar wird, welches Konzept erforderlich ist. Dies dient jedoch dazu, nützliche zusätzliche Informationen in der ersten Zeile der Dokumentation bereitzustellen und Vorwärtserklärungen nicht informativer zu gestalten. Es gibt mehr Dinge, die Sie wissen müssen, als in den Parametertypen gekapselt werden können. Sie müssen also die Dokumente lesen. (Zum Beispiel in Algorithmen, die einen Eingabebereich und einen Ausgabe-Iterator verwenden, benötigt der Ausgabe-Iterator wahrscheinlich genug "Platz" für eine bestimmte Anzahl von Ausgaben, basierend auf der Größe des Eingabebereichs und möglicherweise den darin enthaltenen Werten. Versuchen Sie, diesen stark einzugeben. )
Hier ist Bjarne zu explizit deklarierten Schnittstellen: http://www.artima.com/cppsource/cpp0xP.html
Wenn Sie es anders herum betrachten, können Sie mit duck tippen eine Schnittstelle implementieren, ohne zu wissen, dass die Schnittstelle vorhanden ist. Oder jemand kann eine Schnittstelle absichtlich so schreiben, dass Ihre Klasse sie implementiert, nachdem er Ihre Dokumente konsultiert hat, um festzustellen, dass er nicht nach etwas fragt, was Sie noch nicht getan haben. Das ist flexibel.
quelle
std
Bibliothek, das nicht mit einem Konzept übereinstimmt, ist normalerweise "schlecht geformt, keine Diagnose erforderlich"."OOP bedeutet für mich nur Messaging, lokale Aufbewahrung und Schutz sowie das Verbergen von Staatsprozessen und die extreme Spätbindung aller Dinge. Dies kann in Smalltalk und in LISP erfolgen. Es gibt möglicherweise andere Systeme, in denen dies möglich ist, aber Ich bin mir ihrer nicht bewusst. " - Alan Kay, Schöpfer von Smalltalk.
C ++, Java und die meisten anderen Sprachen sind alle ziemlich weit vom klassischen OOP entfernt. Das Argumentieren für Ideologien ist jedoch nicht besonders produktiv. C ++ ist in keiner Weise rein, daher implementiert es Funktionen, die zu diesem Zeitpunkt pragmatisch sinnvoll erscheinen.
quelle
STL begann mit der Absicht, eine große Bibliothek bereitzustellen, die die am häufigsten verwendeten Algorithmen abdeckt - mit dem Ziel eines konsistenten Verhaltens und einer konsistenten Leistung . Die Vorlage war ein Schlüsselfaktor, um diese Implementierung und dieses Ziel zu verwirklichen.
Nur um eine weitere Referenz zu liefern:
Al Stevens interviewt Alex Stepanov im März 1995 von DDJ:
Stepanov erklärte seine Arbeitserfahrung und seine Entscheidung für eine große Bibliothek von Algorithmen, die sich schließlich zu STL entwickelten.
quelle
Das Grundproblem mit
Wie erhalten Sie sicher den Typ der Sache, die der Iterator zurückgibt? Bei Vorlagen wird dies zur Kompilierungszeit für Sie erledigt.
quelle
Stellen wir uns die Standardbibliothek für einen Moment als eine Datenbank mit Sammlungen und Algorithmen vor.
Wenn Sie die Geschichte von Datenbanken studiert haben, wissen Sie zweifellos, dass Datenbanken zu Beginn meist "hierarchisch" waren. Hierarchische Datenbanken entsprachen sehr genau der klassischen OOP - insbesondere der von Smalltalk verwendeten Sorte mit einfacher Vererbung.
Im Laufe der Zeit stellte sich heraus, dass hierarchische Datenbanken verwendet werden konnten, um fast alles zu modellieren, aber in einigen Fällen war das Einzelvererbungsmodell ziemlich einschränkend. Wenn Sie eine Holztür hatten, war es praktisch, sie entweder als Tür oder als Stück Rohmaterial (Stahl, Holz usw.) betrachten zu können.
Also erfanden sie Netzwerkmodelldatenbanken. Netzwerkmodelldatenbanken entsprechen sehr genau der Mehrfachvererbung. C ++ unterstützt die Mehrfachvererbung vollständig, während Java eine begrenzte Form unterstützt (Sie können nur von einer Klasse erben, aber auch so viele Schnittstellen implementieren, wie Sie möchten).
Sowohl hierarchische Modell- als auch Netzwerkmodelldatenbanken sind größtenteils aus dem allgemeinen Gebrauch verschwunden (obwohl einige in ziemlich spezifischen Nischen verbleiben). Für die meisten Zwecke wurden sie durch relationale Datenbanken ersetzt.
Der Hauptgrund für die Übernahme relationaler Datenbanken war die Vielseitigkeit. Das relationale Modell ist funktional eine Obermenge des Netzwerkmodells (das wiederum eine Obermenge des hierarchischen Modells ist).
C ++ ist weitgehend dem gleichen Weg gefolgt. Die Entsprechung zwischen Einzelvererbung und dem hierarchischen Modell sowie zwischen Mehrfachvererbung und Netzwerkmodell ist ziemlich offensichtlich. Die Entsprechung zwischen C ++ - Vorlagen und dem hierarchischen Modell mag weniger offensichtlich sein, passt aber trotzdem ziemlich gut zusammen.
Ich habe keinen formalen Beweis dafür gesehen, aber ich glaube, dass die Funktionen von Vorlagen eine Obermenge derjenigen sind, die durch Mehrfachvererbung bereitgestellt werden (was eindeutig eine Obermenge einer einzelnen Vererbung ist). Der einzige schwierige Teil ist, dass Vorlagen größtenteils statisch gebunden sind - das heißt, die gesamte Bindung erfolgt zur Kompilierungszeit und nicht zur Laufzeit. Daher kann ein formaler Beweis dafür, dass die Vererbung eine Obermenge der Vererbungsfähigkeiten bietet, durchaus schwierig und komplex sein (oder sogar unmöglich sein).
Auf jeden Fall denke ich, dass dies der Hauptgrund dafür ist, dass C ++ keine Vererbung für seine Container verwendet - es gibt keinen wirklichen Grund dafür, da die Vererbung nur einen Teil der von Vorlagen bereitgestellten Funktionen bietet. Da Vorlagen in einigen Fällen grundsätzlich erforderlich sind, können sie auch fast überall verwendet werden.
quelle
Wie macht man Vergleiche mit ForwardIterator *? Das heißt, wie überprüfen Sie, ob der Artikel, den Sie haben, das ist, wonach Sie suchen, oder ob Sie ihn passiert haben?
Meistens würde ich so etwas verwenden:
Das heißt, ich weiß, dass ich auf MyType zeige, und ich weiß, wie ich diese vergleichen kann. Obwohl es wie eine Vorlage aussieht, ist es nicht wirklich (kein Schlüsselwort "Vorlage").
quelle
Diese Frage hat viele gute Antworten. Es sollte auch erwähnt werden, dass Vorlagen ein offenes Design unterstützen. Beim gegenwärtigen Stand objektorientierter Programmiersprachen muss bei solchen Problemen das Besuchermuster verwendet werden, und echte OOP sollte mehrere dynamische Bindungen unterstützen. Siehe Open Multi-Methods für C ++, P. Pirkelbauer et al. für sehr interessante Lektüre.
Ein weiterer interessanter Punkt von Vorlagen ist, dass sie auch für den Laufzeitpolymorphismus verwendet werden können. Beispielsweise
Beachten Sie, dass diese Funktion auch funktioniert, wenn
Value
es sich um einen Vektor handelt ( nicht um std :: vector, der aufgerufen werden solltestd::dynamic_array
, um Verwirrung zu vermeiden).Wenn
func
es klein ist, wird diese Funktion durch Inlining viel gewinnen. AnwendungsbeispielIn diesem Fall sollten Sie die genaue Antwort kennen (2.718 ...), aber es ist einfach, eine einfache ODE ohne elementare Lösung zu konstruieren (Hinweis: Verwenden Sie ein Polynom in y).
Jetzt haben Sie einen großen Ausdruck in
func
und verwenden den ODE-Solver an vielen Stellen, sodass Ihre ausführbare Datei überall mit Vorlageninstanziierungen verschmutzt wird. Was ist zu tun? Als erstes fällt auf, dass ein regulärer Funktionszeiger funktioniert. Dann möchten Sie Currying hinzufügen, damit Sie eine Schnittstelle und eine explizite Instanziierung schreibenDie obige Instanziierung funktioniert jedoch nur für
double
, warum nicht die Schnittstelle als Vorlage schreiben:und spezialisieren Sie sich auf einige gängige Werttypen:
Wenn die Funktion zuerst um eine Schnittstelle herum entworfen worden wäre, wären Sie gezwungen gewesen, von diesem ABC zu erben. Jetzt haben Sie diese Option sowie Funktionszeiger, Lambda oder ein anderes Funktionsobjekt. Der Schlüssel hier ist, dass wir haben müssen
operator()()
, und wir müssen in der Lage sein, einige arithmetische Operatoren für den Rückgabetyp zu verwenden. Somit würde die Vorlagenmaschinerie in diesem Fall brechen, wenn C ++ keine Operatorüberladung hätte.quelle
Das Konzept, Schnittstelle von Schnittstelle zu trennen und die Implementierungen austauschen zu können, ist für die objektorientierte Programmierung nicht wesentlich. Ich glaube, es ist eine Idee, die in der komponentenbasierten Entwicklung wie Microsoft COM entwickelt wurde. (Siehe meine Antwort zu Was ist komponentengesteuerte Entwicklung?) Als ich aufwuchs und C ++ lernte, wurden die Leute von Vererbung und Polymorphismus hochgespielt. Erst in den 90er Jahren sagten die Leute "Programmieren zu einer 'Schnittstelle', nicht zu einer 'Implementierung'" und "Bevorzugen 'die Objektzusammensetzung' gegenüber 'Klassenvererbung'." (beide übrigens aus GoF zitiert).
Dann kam Java mit integriertem Garbage Collector und
interface
Schlüsselwort, und plötzlich wurde es praktisch, Schnittstelle und Implementierung tatsächlich zu trennen. Bevor Sie es wissen, wurde die Idee Teil der OO. C ++, Vorlagen und STL sind älter als all dies.quelle