it-swarm.dev

Was sind die Vorteile von prototypbasiertem OOP gegenüber klassenbasiertem OOP?

Als ich anfing, Javascript zu programmieren, nachdem ich mich hauptsächlich mit OOP im Kontext klassenbasierter Sprachen) befasst hatte, war ich verwirrt darüber, warum prototypbasierte OOP jemals) gegenüber klassenbasiertem OOP bevorzugt werden.

  1. Was sind die strukturellen Vorteile der Verwendung von prototypbasiertem OOP, falls vorhanden? (Würden wir beispielsweise erwarten, dass es in bestimmten Anwendungen schneller oder weniger speicherintensiv ist?)
  2. Was sind die Vorteile aus der Sicht eines Codierers? (Ist es beispielsweise einfacher, bestimmte Anwendungen mithilfe von Prototyping zu codieren oder den Code anderer Personen zu erweitern?)

Bitte betrachten Sie diese Frage nicht als eine Frage zu Javascript (das im Laufe der Jahre viele Fehler hatte, die völlig unabhängig vom Prototyping sind). Betrachten Sie es stattdessen im Zusammenhang mit den theoretischen Vorteilen des Prototyping gegenüber Klassen.

Vielen Dank.

48
Deets McGeets

Ich hatte ziemlich viel Erfahrung mit beiden Ansätzen, als ich ein RPG-Spiel in Java schrieb. Ursprünglich schrieb ich das ganze Spiel mit klassenbasiertem OOP, erkannte aber schließlich, dass dies der falsche Ansatz war (er wurde mit der Erweiterung der Klassenhierarchie nicht mehr zu warten). Ich habe daher die gesamte Codebasis in prototypbasierten Code konvertiert. Das Ergebnis war viel besser und einfacher zu verwalten.

Quellcode hier, wenn Sie interessiert sind ( Tyrant - Java Roguelike )

Hier sind die Hauptvorteile:

  • Es ist trivial, neue "Klassen" zu erstellen - kopieren Sie einfach den Prototyp und ändern Sie ein paar Eigenschaften und voila ... neue Klasse. Ich habe dies verwendet, um eine neue Art von Trank zu definieren, zum Beispiel in 3-6 Zeilen von Java jeweils. Viel besser als eine neue Klassendatei und jede Menge Boilerplate!
  • Es ist möglich, mit vergleichsweise wenig Code eine extrem große Anzahl von "Klassen" zu erstellen und zu verwalten - Tyrant hatte zum Beispiel etwa 3000 verschiedene Prototypen mit nur etwa 42.000 Zeilen von Code insgesamt. Das ist ziemlich erstaunlich für Java!
  • Mehrfachvererbung ist einfach - Sie kopieren einfach eine Teilmenge der Eigenschaften von einem Prototyp und fügen sie über die Eigenschaften eines anderen Prototyps ein. In einem Rollenspiel möchten Sie beispielsweise, dass ein "Stahlgolem" einige der Eigenschaften eines "Stahlobjekts" und einige der Eigenschaften eines "Golems" sowie einige der Eigenschaften eines "unintelligenten Monsters" aufweist. Einfach mit Prototypen, versuchen Sie das mit einer Erbschaft Hierarchie ......
  • Mit Eigenschaftsmodifikatoren können Sie clevere Dinge tun - indem Sie clevere Logik in die generische Methode "Eigenschaft lesen" einfügen, können Sie verschiedene Modifikatoren implementieren. Zum Beispiel war es einfach, einen magischen Ring zu definieren, der jedem, der ihn trug, +2 Stärke verleiht. Die Logik hierfür befand sich im Ringobjekt nicht in der Methode "Lesestärke", sodass Sie vermieden haben, viele bedingte Tests an anderer Stelle in Ihrer Codebasis durchführen zu müssen (z. B. "trägt das Zeichen" ein Ring der Kraftsteigerung? ")
  • Instanzen können zu Vorlagen für andere Instanzen werden - z. Wenn Sie ein Objekt "klonen" möchten, ist es einfach, das vorhandene Objekt als Prototyp für das neue Objekt zu verwenden. Es ist nicht erforderlich, viele komplexe Klonlogiken für verschiedene Klassen zu schreiben.
  • Es ist ziemlich einfach, das Verhalten zur Laufzeit zu ändern - d. H. Sie können Eigenschaften ändern und ein Objekt zur Laufzeit ziemlich willkürlich "verwandeln". Ermöglicht coole Effekte im Spiel, und wenn Sie dies mit einer "Skriptsprache" koppeln, ist zur Laufzeit so ziemlich alles möglich.
  • Es ist eher für einen "funktionalen" Programmierstil geeignet - Sie neigen dazu, viele Funktionen zu schreiben, die Objekte und Handlungen angemessen analysieren, anstatt eingebettete Logik in Methoden, die bestimmten Klassen zugeordnet sind. Ich persönlich bevorzuge diesen FP Stil).

Hier sind die Hauptnachteile:

  • Sie verlieren die Sicherheit der statischen Typisierung , da Sie effektiv ein dynamisches Objektsystem erstellen. Dies bedeutet in der Regel, dass Sie mehr Tests schreiben müssen, um sicherzustellen, dass das Verhalten korrekt ist und dass Objekte von der richtigen "Art" sind.
  • Es gibt einen gewissen Leistungsaufwand - da Lesevorgänge von Objekteigenschaften im Allgemeinen gezwungen sind, eine oder mehrere Kartensuchen durchzuführen, zahlen Sie einen geringen Leistungsaufwand . In meinem Fall war es kein Problem, aber es könnte in einigen Fällen ein Problem sein (z. B. ein 3D-FPS mit vielen Objekten, die in jedem Frame abgefragt werden).
  • Refactorings funktionieren nicht auf die gleiche Weise - in einem prototypbasierten System "bauen" Sie im Wesentlichen Ihre Vererbungserbschaft mit Code auf. IDEs/Refactoring-Tools können Ihnen nicht wirklich helfen, da sie Ihren Ansatz nicht beeinflussen können. Ich fand das nie ein Problem, aber es könnte außer Kontrolle geraten, wenn Sie nicht vorsichtig sind. Sie möchten wahrscheinlich, dass Tests überprüfen, ob Ihre Vererbungshierarchie korrekt erstellt wird!
  • Es ist ein bisschen fremd - Leute, die an einen konventionellen Stil gewöhnt sind OOP) können leicht verwirrt werden. "Was meinst du damit? nur eine Klasse namens "Thing"?!? "-" Wie erweitere ich diese letzte Thing-Klasse!?! "-" Sie verletzen OOP Prinzipien !!! "-" Es ist falsch zu haben all diese statischen Funktionen, die auf jede Art von Objekt wirken!?!? "

Zum Schluss noch einige Implementierungshinweise:

  • Ich habe eine Java HashMap für Eigenschaften und einen "übergeordneten" Zeiger für den Prototyp verwendet. Dies hat gut funktioniert, hatte aber die folgenden Nachteile: a) Eigenschaftslesevorgänge mussten manchmal über eine lange übergeordnete Kette zurückverfolgt werden. Leistungseinbußen b) Wenn Sie einen übergeordneten Prototyp mutiert haben, wirkt sich die Änderung auf alle Kinder aus, die die sich ändernde Eigenschaft nicht überschrieben haben. Dies kann zu subtilen Fehlern führen, wenn Sie nicht vorsichtig sind!
  • Wenn ich das noch einmal machen würde, würde ich eine unveränderliche persistente Map für die Eigenschaften verwenden (ähnlich wie Clojures persistente Maps ), oder meine eigene Java) dauerhafte Implementierung der Hash-Map . Dann würden Sie den Vorteil billiger Kopien/Änderungen in Verbindung mit unveränderlichem Verhalten erhalten und müssten Objekte nicht dauerhaft mit ihren Eltern verknüpfen.
  • Sie können Spaß haben, wenn Sie Funktionen/Methoden in Objekteigenschaften einbetten. Der Hack, den ich in Java für diese (anonyme Untertypen einer "Script" -Klasse)) verwendet habe, war nicht sehr elegant - wenn ich das noch einmal mache, würde ich wahrscheinlich eine richtige, leicht einbettbare Sprache für verwenden Skripte (Clojure oder Groovy)
47
mikera

Der Hauptvorteil von prototypbasiertem OOP ist, dass Objekte & "Klassen" zur Laufzeit erweitert werden können.

In Class-based OOP gibt es einige gute Funktionen, die leider von der Programmiersprache abhängen.

Object Pascal (Delphi), VB.Net & C # bietet eine sehr direkte Möglichkeit, Eigenschaften (nicht zu verwechseln mit Feldern) und Zugriffsmethoden von Eigenschaften zu verwenden, während auf Eigenschaften Java & C++) zugegriffen wird durch Methoden. Und PHP hat eine Mischung aus beiden, genannt "magische Methoden".

Es gibt einige dynamische Schreibklassen, obwohl die Hauptklasse O.O. Sprachen haben statische Typisierung. Ich denke, statisches Tippen mit Klasse O.O. Es ist sehr nützlich, da es eine Funktion namens Object Introspection ermöglicht, mit der diese I.D.E. und Webseiten visuell und schnell entwickeln.

2
umlcat

Ich muss @umlcat zustimmen. Klassenerweiterung ist ein großer Vorteil. Angenommen, Sie möchten einer Zeichenfolgenklasse über einen längeren Zeitraum mehr Funktionen hinzufügen. In C++ würden Sie dies durch fortgesetzte Vererbung früherer Zeichenfolgenklassengenerationen tun. Das Problem bei diesem Ansatz ist, dass jede Generation im Wesentlichen zu einem eigenen Typ wird, was zu einem massiven Umschreiben vorhandener Codebasen führen kann. Mit der prototypischen Vererbung "hängen" Sie einfach eine neue Methode an die ursprüngliche Basisklasse an ... kein massiver Aufbau geerbter Klassen und Vererbungsbeziehungen überall. Ich würde gerne sehen, dass C++ in seinem neuen Standard einen ähnlichen Erweiterungsmechanismus entwickelt. Aber ihr Komitee scheint von Leuten geleitet zu werden, die die eingängigen und beliebten Features hinzufügen wollen.

0
annoying_squid