Ich hoffe, dass diese Streifzüge meine Frage klarstellen werden - ich würde es vollkommen verstehen, wenn sie es nicht tun. Lassen Sie mich wissen, ob dies der Fall ist, und ich werde versuchen, mich klarer zu machen.
Lernen Sie BoxPong kennen , ein sehr einfaches Spiel, das ich entwickelt habe, um mich mit der objektorientierten Spieleentwicklung vertraut zu machen. Ziehen Sie die Box, um den Ball zu kontrollieren und gelbe Gegenstände zu sammeln.
Mit BoxPong konnte ich unter anderem eine grundlegende Frage formulieren: Wie kann ich Objekte haben, die miteinander interagieren, ohne zueinander "gehören" zu müssen? Mit anderen Worten, gibt es eine Möglichkeit, dass Objekte nicht hierarchisch sind, sondern koexistieren? (Ich werde weiter unten näher darauf eingehen.)
Ich vermute, dass das Problem der Koexistenz von Objekten weit verbreitet ist, daher hoffe ich, dass es einen etablierten Weg gibt, es zu lösen. Ich möchte das Vierkantrad nicht neu erfinden, daher denke ich, dass die ideale Antwort, die ich suche, lautet: "Hier ist ein Entwurfsmuster, das üblicherweise zur Lösung Ihrer Art von Problem verwendet wird."
Insbesondere in einfachen Spielen wie BoxPong ist klar, dass es eine Handvoll Objekte gibt oder geben sollte, die auf derselben Ebene nebeneinander existieren. Es gibt eine Kiste, es gibt einen Ball, es gibt ein Sammlerstück. Alles, was ich in objektorientierten Sprachen ausdrücken kann, sind strenge HAS-A- Beziehungen. Dies geschieht über Mitgliedsvariablen. Ich kann nicht einfach anfangen ball
und es sein Ding machen lassen, ich brauche es, um dauerhaft zu einem anderen Objekt zu gehören . Ich habe es so eingerichtet, dass das Hauptspiel Objekt hat eine Box, und die Box wiederum hat einen Ball und hat einen Score - Zähler. Jedes Objekt hat auch eineupdate()
Methode, die Position, Richtung usw. berechnet, und ich gehe dort einen ähnlichen Weg: Ich rufe die Aktualisierungsmethode des Hauptspielobjekts auf, die die Aktualisierungsmethoden aller seiner untergeordneten Elemente aufruft, und sie rufen wiederum die Aktualisierungsmethoden aller ihrer untergeordneten Elemente auf . Dies ist der einzige Weg, den ich sehen kann, um ein objektorientiertes Spiel zu machen, aber ich denke, es ist nicht der ideale Weg. Schließlich würde ich den Ball nicht genau als zur Box gehörend betrachten, sondern als auf der gleichen Ebene und mit ihm interagierend. Ich nehme an, dass dies erreicht werden kann, indem alle Spielobjekte in Mitgliedsvariablen des Hauptspielobjekts umgewandelt werden, aber ich sehe keine Lösung dafür. Ich meine ... abgesehen von der offensichtlichen Unordnung, wie würde es für den Ball und die Box eine Möglichkeit geben, sich zu kennen , das heißt zu interagieren?
Es gibt auch das Problem, dass Objekte Informationen untereinander weitergeben müssen. Ich habe ziemlich viel Erfahrung mit dem Schreiben von Code für das SNES, bei dem Sie praktisch jederzeit auf praktisch den gesamten RAM zugreifen können. Angenommen, Sie machen einen benutzerdefinierten Feind für Super Mario World und möchten, dass alle Münzen von Mario entfernt werden. Speichern Sie dann einfach Null, um $ 0DBF zu adressieren, kein Problem. Es gibt keine Einschränkungen, die besagen, dass Feinde nicht auf den Status des Spielers zugreifen können. Ich glaube, ich bin von dieser Freiheit verwöhnt worden, weil ich mich bei C ++ und dergleichen oft frage, wie ich einen Wert für andere Objekte (oder sogar global) zugänglich machen kann.
Was wäre, wenn ich am Beispiel von BoxPong wollte, dass der Ball von den Rändern des Bildschirms abprallt? width
und height
sind Eigenschaften der Game
Klasse,ball
Zugang zu ihnen haben. Ich könnte diese Art von Werten weitergeben (entweder über Konstruktoren oder die Methoden, wo sie benötigt werden), aber das schreit mir nur nach schlechter Praxis.
Ich denke, mein Hauptproblem ist, dass ich Objekte brauche, um mich zu kennen, aber der einzige Weg, den ich sehen kann, ist eine strenge Hierarchie, die hässlich und unpraktisch ist.
Ich habe von "Freundschaftsklassen" in C ++ gehört und weiß irgendwie, wie sie funktionieren, aber wenn sie die Endlösung sind, warum sehe ich dann nicht, dass friend
Schlüsselwörter über jedes einzelne C ++ - Projekt verteilt werden, und wie kommt es, dass Konzept existiert nicht in jeder OOP-Sprache? (Gleiches gilt für Funktionszeiger, von denen ich erst kürzlich erfahren habe.)
Vielen Dank im Voraus für Antworten jeglicher Art - und wenn es einen Teil gibt, der für Sie keinen Sinn ergibt, lassen Sie es mich wissen.
Antworten:
Im Allgemeinen stellt sich heraus, dass Objekte derselben Ebene voneinander wissen. Sobald Objekte voneinander wissen, werden sie gebunden oder miteinander gekoppelt . Dies macht es schwierig, sie zu ändern, zu testen und zu warten.
Es funktioniert viel besser, wenn es ein Objekt "oben" gibt, das über die beiden Bescheid weiß und die Interaktionen zwischen ihnen festlegen kann. Das Objekt, das die beiden Peers kennt, kann sie durch Abhängigkeitsinjektion oder über Ereignisse oder durch Nachrichtenübermittlung (oder einen anderen Entkopplungsmechanismus) miteinander verbinden. Ja, das führt zu einer künstlichen Hierarchie, aber es ist weitaus besser als das Spaghetti-Chaos, das man bekommt, wenn die Dinge nur wohl oder übel interagieren. Dies ist in C ++ nur wichtiger, da Sie auch für die Lebensdauer der Objekte etwas benötigen.
Kurz gesagt, Sie können dies tun, indem Sie Objekte überall nebeneinander durch Ad-hoc-Zugriff miteinander verbinden, aber es ist eine schlechte Idee. Die Hierarchie sorgt für Ordnung und klare Eigenverantwortung. Das Wichtigste ist, dass Objekte im Code nicht unbedingt Objekte im wirklichen Leben (oder sogar im Spiel) sind. Wenn die Objekte im Spiel keine gute Hierarchie bilden, ist eine andere Abstraktion möglicherweise besser.
quelle
Nein!
Ich denke, das Hauptproblem, das Sie haben, ist, dass Sie "Objektorientierte Programmierung" etwas zu wörtlich nehmen. In OOP stellt ein Objekt kein "Ding" dar, sondern eine "Idee", dh "Ball", "Spiel", "Physik", "Mathematik", "Datum" usw. Alle sind gültige Objekte. Es ist auch nicht erforderlich, dass Objekte über irgendetwas "Bescheid wissen".
Date.Now().getTommorrow()
Fragen Sie beispielsweise den Computer, welcher Tag heute ist, wenden Sie arkane Datumsregeln an, um das Datum von morgen zu ermitteln, und geben Sie dies an den Anrufer zurück. DasDate
Objekt weiß nichts anderes, es muss nur Informationen nach Bedarf vom System anfordern. AußerdemMath.SquareRoot(number)
muss nichts außer der Logik zur Berechnung einer Quadratwurzel bekannt sein.In Ihrem Beispiel, das ich zitiert habe, sollte der "Ball" nichts über die "Box" wissen. Boxen und Bälle sind völlig unterschiedliche Ideen und haben kein Recht miteinander zu reden. Aber eine Physik-Engine weiß, was eine Box und ein Ball sind (oder zumindest ThreeDShape), und sie weiß, wo sie sich befinden und was mit ihnen geschehen soll. Wenn der Ball also schrumpft, weil es kalt ist, würde die Physik-Engine dieser Ballinstanz mitteilen, dass er jetzt kleiner ist.
Es ist ein bisschen wie ein Auto zu bauen. Ein Computerchip weiß nichts über einen Automotor, aber ein Auto kann einen Computerchip verwenden, um einen Motor zu steuern. Die einfache Idee, kleine, einfache Dinge zusammen zu verwenden, um ein etwas größeres, komplexeres Ding zu schaffen, das selbst als Bestandteil anderer komplexerer Teile wiederverwendbar ist.
Und was ist in Ihrem Mario-Beispiel, wenn Sie sich in einem Herausforderungsraum befinden, in dem das Berühren eines Feindes keine Marios-Münzen verbraucht, sondern ihn nur aus diesem Raum auswirft? Außerhalb des Ideenraums von Mario oder dem Feind sollte Mario Münzen verlieren, wenn er einen Feind berührt (wenn Mario einen Unverwundbarkeitsstern hat, tötet er stattdessen den Feind). Welches Objekt (Domäne / Idee), das für das verantwortlich ist, was passiert, wenn Mario einen Feind berührt, ist das einzige, das über eines von beiden Bescheid wissen muss und was auch immer es mit einem von beiden tun sollte (sofern dieses Objekt externe Änderungen zulässt) ).
Auch bei Ihren Aussagen zu Objekten, die Kinder aufrufen
Update()
, ist dies äußerst fehleranfällig, als würdeUpdate
es mehrmals pro Frame von verschiedenen Eltern aufgerufen? (Selbst wenn Sie dies bemerken, ist dies verschwendete CPU-Zeit, die Ihr Spiel verlangsamen kann.) Jeder sollte nur berühren, was er braucht, wenn er muss. Wenn Sie Update () verwenden, sollten Sie eine Art Abonnementmuster verwenden, um sicherzustellen, dass alle Updates einmal pro Frame aufgerufen werden (wenn dies für Sie nicht wie in Unity behandelt wird).Das Erlernen der Definition Ihrer Domain-Ideen in klare, isolierte, gut definierte und benutzerfreundliche Blöcke ist der wichtigste Faktor dafür, wie gut Sie OOP nutzen können.
quelle
Sie scheinen den Punkt der objektorientierten Programmierung zu verfehlen.
Bei der objektorientierten Programmierung geht es darum, Abhängigkeiten zu verwalten, indem bestimmte Schlüsselabhängigkeiten in Ihrer Architektur selektiv invertiert werden, um Starrheit, Fragilität und Nichtwiederverwendbarkeit zu vermeiden.
Was ist Abhängigkeit? Abhängigkeit ist das Vertrauen auf etwas anderes. Wenn Sie Null speichern, um $ 0DBF zu adressieren, verlassen Sie sich auf die Tatsache, dass sich in dieser Adresse Marios Münzen befinden und die Münzen als Ganzzahl dargestellt werden. Ihr Custom Enemy-Code hängt vom Code ab, der Mario und seine Münzen implementiert. Wenn Sie ändern, wo Mario seine Münzen speichert, müssen Sie den gesamten Code, der auf den Speicherort verweist, manuell aktualisieren.
Bei objektorientiertem Code geht es darum, Ihren Code von Abstraktionen und nicht von Details abhängig zu machen. Also statt
du würdest schreiben
Wenn Sie nun ändern möchten, wie Mario seine Münzen von einem int in einen long oder einen double speichert, oder sie im Netzwerk speichern oder in einer Datenbank speichern oder einen anderen langen Prozess starten möchten, nehmen Sie die Änderung an einem Ort vor: dem Mario-Klasse und all Ihr anderer Code funktioniert ohne Änderungen.
Deshalb, wenn Sie fragen
Sie fragen wirklich:
Das ist keine objektorientierte Programmierung.
Ich schlage vor, Sie lesen zunächst alles hier: http://objectmentor.com/omSolutions/oops_what.html und suchen dann auf YouTube nach allem von Robert Martin und sehen sich alles an.
Meine Antworten stammen von ihm, und einige davon werden direkt von ihm zitiert.
quelle
mario.coins = 0;
, abermario.loseCoins();
was in Ordnung und wahr ist - aber mein Punkt ist, wie kann der Feind überhaupt Zugriff auf dasmario
Objekt haben?mario
eine Mitgliedsvariable von zu seinenemy
, scheint mir nicht richtig zu sein.Sie können die lose Kopplung aktivieren, indem Sie das Mediatormuster anwenden. Für die Implementierung müssen Sie einen Verweis auf eine Mediatorkomponente haben, die alle empfangenden Komponenten kennt.
Es erkennt den Gedanken "Spielleiter, bitte lass dies und das geschehen".
Ein verallgemeinertes Muster ist das Publish-Subscribe-Muster. Es ist angemessen, wenn der Mediator nicht viel Logik enthalten sollte. Verwenden Sie andernfalls einen handgefertigten Mediator, der weiß, wohin alle Anrufe weitergeleitet und möglicherweise sogar geändert werden müssen.
Es gibt synchrone und asynchrone Varianten, die normalerweise als Ereignisbus, Nachrichtenbus oder Nachrichtenwarteschlange bezeichnet werden. Suchen Sie nach ihnen, um festzustellen, ob sie für Ihren speziellen Fall geeignet sind.
quelle