"instanceof" loswerden

8

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:

  • Ich glaube nicht, dass es besser ist, einfach eine isABox() -Methode zur Sprite -Schnittstelle hinzuzufügen.
  • Wäre es hilfreich, wenn Box eine Schnittstelle wäre, damit andere Klassen dasselbe Privileg bekommen könnten?
  • Sollte ich versuchen, etwas Fantastisches zu tun, wie Mustervergleich / Doppelversand, mit Besuchern wie Mustern?
  • Ist es in Ordnung, dass das Regelmodul eng mit den Typen zusammenarbeitet, einfach weil es ihre Semantik sowieso wissen soll?
  • Ist die gesamte Idee eines Regelmodul-Strategie-Musters fehlerhaft?
  • Es macht keinen Sinn, die Regeln in Sprites zu erstellen, da sie dann alle geändert werden müssen, wenn ein neuer Typ hinzugefügt wird.

Ich hoffe, Sie haben etwas Ähnliches versucht und sind in der Lage, mir in die richtige Richtung zu zeigen.

    
Thomas Ahle 03.01.2012, 11:32
quelle

11 Antworten

3

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.

    
Tomas Narros 03.01.2012, 13:13
quelle
7

Verwenden Sie Polymorphismus :

%Vor%

Sie müssen also in Ihrem Beispiel nur sprite.someMethod () aufrufen.

    
Artem 03.01.2012 11:36
quelle
4

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:

    %Vor%

    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.

    
Chris Burt-Brown 03.01.2012 16:45
quelle
3

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?).

    
lsoliveira 03.01.2012 11:37
quelle
1

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.

    
Michael Borgwardt 03.01.2012 11:42
quelle
1

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%     
Peter Lawrey 03.01.2012 12:16
quelle
1

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

%Vor%

Sie können dann eine andere Art von "gewinnendem" Sprite hinzufügen, ohne die Funktion isGameWon zu ändern.

    
Chris Burt-Brown 03.01.2012 14:54
quelle
1

Wie wäre es mit einem Besucher.

Jeder Punkt erbt die acceptBoxDetection-Methode:

%Vor%     
Emile 15.11.2013 20:12
quelle
0

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.

    
Matthias 03.01.2012 11:37
quelle
0

Ich denke, was die Leute Ihnen vorgeschlagen haben, ist richtig. Ihr doSomething() kann wie folgt aussehen:

%Vor%

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%     
Mr.J4mes 03.01.2012 11:56
quelle
-1

Wenn Sie sagen "Stack" und "der oberste zählt", können Sie nicht einfach den obersten nehmen und etwas damit machen?

    
DerMike 03.01.2012 11:38
quelle

Tags und Links