In einem Sprite-basierten Spiel, das ich schreibe, enthält jedes Feld in einem 2D-Gitter einen Stapel Sprites. Meistens zählt der oberste.
Im Regelmodul des Spiels habe ich eine Menge Code wie folgt:
%Vor% Upadate: //Do something
zählt, wenn ein Box
über jedem Target
steht. Ich sehe nicht, wie das geht, indem einfach doSomething()
zu Sprite hinzugefügt wird, es sei denn, doSomething()
gibt 1 zurück, wenn das Sprite eine Box ist, andernfalls 0. (und das wäre genauso wie instanceof).
Ich weiß, dass instanceof als schädlich angesehen wird, weil es die Idee der objektorientierten Programmierung umbringt.
Ich bin mir jedoch nicht sicher, wie ich den Code in meinem Fall beheben soll. Hier sind einige Gedanken, die ich hatte:
isABox()
-Methode zur Sprite
-Schnittstelle hinzuzufügen. Box
eine Schnittstelle wäre, damit andere Klassen dasselbe Privileg bekommen könnten? Ich hoffe, Sie haben etwas Ähnliches versucht und sind in der Lage, mir in die richtige Richtung zu zeigen.
Hier ist mein Versuch. Erwägen Sie, eine Enum mit den verschiedenen Sprite-Typen zu definieren:
%Vor%Und zuletzt:
%Vor%Auf diese Weise können Sie das Problem lösen, dass Sie für jeden Typ X in Sprite eine isATypeX () -Methode erstellen müssen. Wenn Sie einen neuen Typ benötigen, fügen Sie der Enumeration einen neuen Wert hinzu, und nur die Regel, die diesen Typ überprüfen muss, muss dies wissen.
Verwenden Sie Polymorphismus :
%Vor%Sie müssen also in Ihrem Beispiel nur sprite.someMethod () aufrufen.
Instanceof
: (Fast) immer schädlich
Ich habe mir alle Antworten auf Ihren Beitrag angesehen und versucht zu verstehen, was Sie getan haben. Und ich bin zu dem Schluss gekommen, dass instanceof
genau ist, was Sie wollen und Ihr ursprüngliches Codebeispiel war in Ordnung.
Sie haben das klargestellt:
Sie verstoßen nicht gegen das Liskov-Substitutionsprinzip, da keiner der Box-Codes den Sprite-Code ungültig macht.
Sie geben nicht den Code mit der Antwort auf instanceof
an. Deshalb sagen Leute, instanceof ist schlecht; weil Leute das tun:
Dies ist der Grund, warum Leute instanceof vermeiden. Aber das machst du nicht.
Es gibt eine eins-zu-eins Beziehung zwischen Box
und dem Gewinn des Spiels (kein anderer Sprite kann das Spiel gewinnen). Sie brauchen also keine zusätzliche "Gewinner" -Sprite-Abstraktion (weil Boxen == Gewinner).
Sie überprüfen einfach das Board, um sicherzustellen, dass jedes oberste Element ein Box ist. Genau dafür ist instanceof
vorgesehen.
Die Antwort aller anderen (einschließlich meiner eigenen) fügt einen zusätzlichen Mechanismus hinzu, um zu prüfen, ob ein Sprite eine Box ist. Sie fügen jedoch keine Robustheit hinzu. Tatsächlich nehmen Sie Funktionen, die bereits von der Sprache geliefert werden, und implementieren sie in Ihrem eigenen Code neu.
Tomas Narros argumentiert, dass Sie in Ihrem Code zwischen "semantischen Typen" und "Java-Typen" unterscheiden sollten. Ich bin nicht einverstanden. Sie haben bereits festgestellt, dass Sie einen Java-Typ haben, Box
, welche Unterklassen Sprite
. So haben Sie bereits alle Informationen, die Sie brauchen.
Meiner Meinung nach verstößt ein zweiter unabhängiger Mechanismus, der auch "Ich bin eine Kiste" meldet, gegen DRY (Do not Repeat Yourself). Dies bedeutet, dass Sie nicht zwei unabhängige Quellen für die gleiche Information haben. Sie müssen nun eine Enum- und eine Klassenstruktur pflegen.
Der sogenannte "Vorteil" ist die Fähigkeit, um ein Schlüsselwort zu pirouetten, das den Zweck vollständig erfüllt, und schädlich ist, wenn es auf schädlichere Weise verwendet wird.
Die goldene Regel ist Benutze deinen Kopf . Befolgen Sie keine Regeln als harte Fakten. Frage sie, lerne, warum sie da sind, und verbiege sie, wenn es angebracht ist.
Grundlegende Überladung ist der Weg dorthin. Es ist die Sprite-Klassenhierarchie, die wissen sollte, was zu tun ist und wie es gemacht wird, wie in:
%Vor% BEARBEITEN:
Ihre Bearbeitung der Frage ändert das Problem nicht grundlegend. Was Sie haben, ist eine Logik, die nur für Box
gilt. Verkapseln Sie diese spezielle Logik erneut in der Box
-Instanz (siehe oben). Sie könnten weiter gehen und eine generische Oberklasse für Ihre Sprites erstellen, die einen Standardwert für isCountable()
definiert (beachten Sie, dass die Methode ähnlich wie die isBox ist, aber aus Designsicht besser ist, da es für einen Kreis keinen Sinn ergibt) Haben Sie eine IsBox-Methode - sollte Box auch eine isCircle-Methode enthalten?).
Grundsätzlich statt
%Vor%Verwenden Sie
%Vor% Dabei ist doSomething()
in Sprite
definiert und in Box
überschrieben.
Wenn diese Regeln von der Sprite-Klassenhierarchie getrennt werden sollen, können Sie sie in eine separate Rules
-Klasse (oder -Schnittstelle) verschieben, wobei Sprite
eine getRules()
-Methode hat und Unterklassen unterschiedliche Implementierungen zurückgeben. Dies würde die Flexibilität weiter erhöhen, da Objekte der gleichen Unterklasse Sprite
ein anderes Verhalten aufweisen können.
Hier ist ein Beispiel für einen generischen Zähler ohne eine isAnX () -Methode für jeden Typ, den Sie zählen möchten. Angenommen, Sie möchten die Nummer des Typs X auf dem Board zählen.
%Vor%Ich vermute, was du wirklich willst ist
%Vor%Was Sie wirklich hier testen, ist
Kann der Spieler das Spiel mit diesem
Sprite
an der Spitze des Bretts gewinnen?
Daher schlage ich diese Namen vor:
%Vor% Es hat absolut keinen Sinn, eine Funktion isBox
zu haben. Überhaupt keine. Sie können auch instanceof
verwenden.
Aber wenn Box
, Bottle
und Target
alle gewinnenden Kacheln sind, können Sie sie alle zurückgeben lassen
Sie können dann eine andere Art von "gewinnendem" Sprite hinzufügen, ohne die Funktion isGameWon
zu ändern.
Allgemeine Aussagen über objektorientiertes Design / Refactorings sind für IMHO schwer zu treffen, da die "beste" Aktion sehr vom Kontext abhängt.
Sie sollten versuchen, das "Mach etwas" in eine virtuelle Methode von Sprite zu verschieben, die nichts tut. Diese Methode kann innerhalb Ihrer Schleife aufgerufen werden.
Box kann es dann überschreiben und das "etwas" tun.
Ich denke, was die Leute Ihnen vorgeschlagen haben, ist richtig. Ihr doSomething()
kann wie folgt aussehen:
Also in Ihrem ursprünglichen Code können Sie dies tun:
%Vor%Oder Sie können das gleiche Ziel auch mit dem erreichen, was Sie vorgeschlagen haben, aber es kann eine zusätzliche Berechnung pro Schleife kosten.
%Vor%Tags und Links java instanceof types sprite