2015-06-17 12 views
8

Das Szenario. Ich schreibe spielbezogenen Code. In diesem Spiel hat eine Player (es ist auch eine Klasse) eine Liste von Item. Es gibt andere Typen von Elementen, die von Item erben, beispielsweise ContainerItem, DurableItem oder WeaponItem.Ist es ratsam, instanceof häufig zu verwenden?

Offensichtlich ist es sehr günstig für mich, nur List<Item> zu haben. Aber wenn ich die Gegenstände des Spielers bekomme, kann ich nur anhand des Schlüsselworts instanceof zwischen der Art des Gegenstands unterscheiden. Ich bin sicher, ich habe gelesen, dass es eine schlechte Übung ist, sich darauf zu verlassen.

Ist es in Ordnung, es in diesem Fall zu verwenden? Oder sollte ich meine gesamte Struktur überdenken?

+0

können Sie die Notwendigkeit vermeiden instanceof verwenden, wenn Item eine abstrakte Methode definiert, zwingt jede Unterklasse ihre eigene zu implementieren. – NickJ

+0

Überdenken Sie wahrscheinlich Ihre Struktur. Das Muster, das du beschreibst, ist wirklich ein Anti-Muster. – MadConan

+0

Ja, die Verwendung von 'instanceof' ist im Allgemeinen ein Symptom für ein schlechtes/schlechtes Design. Der Trick besteht darin, doppelte Versand- oder Besuchermuster zu verwenden. Schau im Internet nach. –

Antwort

10

Lassen Sie uns sagen, dass ich etwas Inventar Code schreibe:

public void showInventory(List<Item> items) { 
    for (Item item : items) { 
     if (item instanceof ContainerItem) { 
      // container display logic here 
     } 
     else if (item instanceof WeaponItem) { 
      // weapon display logic here 
     } 
     // etc etc 
    } 
} 

Das wird kompilieren und gut funktionieren. Aber es fehlt eine Schlüsselidee des objektorientierten Designs: Sie können übergeordnete Klassen definieren, um allgemeine nützliche Dinge zu tun, und untergeordnete Klassen müssen bestimmte, wichtige Details ausfüllen.

Alternative Ansatz oben:

abstract class Item { 
    // insert methods that act exactly the same for all items here 

    // now define one that subclasses must fill in themselves 
    public abstract void show() 
} 
class ContainerItem extends Item { 
    @Override public void show() { 
     // container display logic here instead 
    } 
} 
class WeaponItem extends Item { 
    @Override public void show() { 
     // weapon display logic here instead 
    } 
} 

Jetzt haben wir einen Ort zu suchen, die show() Methode, in allen untergeordneten Klassen für die Bestandsanzeigelogik. Wie greifen wir darauf zu? Einfach!

public void showInventory(List<Item> items) { 
    for (Item item : items) { 
     item.show(); 
    } 
} 

Wir behalten die gesamte itemspezifische Logik in spezifischen Item-Unterklassen. Dies macht Ihre Codebasis einfacher zu pflegen und zu erweitern. Es reduziert die kognitive Belastung der langen for-each-Schleife im ersten Codebeispiel. Und es bereitet show() vor, an Orten wiederverwendbar zu sein, die Sie noch nicht einmal entworfen haben.

+0

Dies ist der korrekte Weg, aber manchmal ist es nicht genug. Nehmen wir zum Beispiel die Klasse 'Record' - wir haben 2 Arten von Datensätzen -' File' und 'Directory'. Der übliche Kram kann im Datensatz abstrahiert werden, aber sagen wir, 'Datei' hat eine' download() 'Methode. Von einem 'Set ' können Sie die Dateien nur herunterladen, wenn Sie wissen, dass es sich tatsächlich um Dateien handelt. Also, wenn Sie in diesen Fall fallen, ist Polymorphie nicht genug, aber in den meisten Fällen ist es die beste Lösung, also +1 für die gute Antwort –

+0

@SvetlinZarev Sie können das auch abstrahieren - durch die Implementierung von Methoden, die sinnvoll sind, entweder UnsupportedOperationException (z. B. java.util.Iterator.remove), eine Null-Objekt/Default-Rückgabe oder Hinzufügen von Methoden zur Abfrage, wenn eine Methode anwendbar ist. Alles eine Frage der Anstrengung, keine prinzipielle Unmöglichkeit. – Durandal

+1

Ich halte es für ein schlechtes Design, Methoden zu einer Schnittstelle hinzuzufügen, nur um 'UnsupportedOperationException' zu werfen. Auch der Zweck des Nullobjektmusters ist völlig anders. Ja, es könnte den Job erledigen, aber es wird mehr wie ein Hack, als eine flächendeckende Lösung. Nehmen Sie zum Beispiel mein 'Record' Beispiel. Warum sollte ich die "Record" -Schnittstelle mit Methoden verunreinigen, die nicht für jeden Datensatz gelten? Dadurch mache ich nur die API schwieriger zu verwenden - d. H. Die Clients müssen sich mit dummen Ausnahmen oder usless-Rückgabewerten befassen. –

2

Sie sollten Ihre Struktur überdenken, instanceof in Nicht-Meta-Code ist fast immer ein Zeichen für ein Anti-Muster. Versuchen Sie, das Verhalten zu definieren, das alle Item s haben (wie ein Bild, eine Beschreibung und etwas passiert, wenn Sie auf sie klicken) in der Item -Klasse/Schnittstelle, die Verwendung der abstract -Keyword wenn geeignet, und dann polymorphism verwenden um die Besonderheiten umzusetzen.

2

IMHO mit instanceof ist ein Code-Geruch. Einfach gesagt - es macht Ihren Code prozedural, nicht objektorientiert. Die OO-Art, dies zu tun, ist die visitor pattern.

enter image description here

Das Besuchermuster ermöglicht es Ihnen auch leicht decorator s und chain of responsibility oben drauf zu bauen, also Trennung von Bereichen zu erzielen, die in kürzeren, sauberere Ergebnisse und leichter zu lesen und Testcode.

Auch Sie wirklich müssen die genaue Klasse wissen? Kannst du Polymorphismus nicht ausnutzen? Immerhin ist Axe ein Weapon genauso wie Sword ist.

+7

IMO, das Besuchermuster ist großartig So sichern Sie Ihren Job - niemand kann Ihren Code verstehen! – NickJ

+0

Ich stimme nicht zu, das Viditor-Muster ermöglicht es Ihnen, "Dekorateur" und "Verantwortungskette" ganz einfach zu erstellen, um eine "Trennung von Bedenken" zu erreichen und Ihren Code dadurch einfacher und einfacher zu lesen. Es erzwingt auch Typ Sicherheit - Sie können keine neue Unterklasse von T hinzufügen, ohne auf einen Kompilierfehler zu stolpern, während "instanceoff" Sie das Problem so spät wie während der Laufzeit entdecken –

+0

Mein Kommentar war nicht ernst, aber ich habe es wiederholt versucht Kopf um das Besuchermuster herum, und nie erfolgreich. Ich musste Code mit dem Besuchermuster pflegen und fand es frustrierend. – NickJ

3

Sie sollten vielleicht überdenken und versuchen, Polymorphie zu verwenden, um Ihre Idee List<Item> zu implementieren.

Hier einige Referenzen für Ihr Problem, das wahrscheinlich helfen können:


(Referenzen von Is instanceof considered bad practice? If so, under what circumstances is instanceof still preferable?)

0

Es ist in Ordnung, wenn es für Sie einfach zu verstehen ist.

Bewegliche Verzweigungslogiken von wo sie natürlich alle zu Unterklassen gehören, ist nicht unbedingt eine gute Idee. Es kann die falsche Abhängigkeit verursachen; es kann zu aufgeblähten Klassen führen, und es kann schwierig sein, zu navigieren und zu verstehen.

Am Ende geht es darum, unseren Code mit mehreren Dimensionen von Bedenken in einem eindimensionalen Raum physikalisch zu organisieren. Es ist kein triviales Problem, es ist subjektiv und es gibt kein Allheilmittel.

Insbesondere erbte unsere Industrie eine Menge von Daumenregeln, die im letzten Jahrhundert auf der Grundlage von technischen und wirtschaftlichen Einschränkungen dieser Ära gemacht wurden. Zum Beispiel waren die Werkzeuge sehr begrenzt; Programmierer waren sehr teuer; Anwendungen entwickelten sich langsam.

Einige dieser Regeln gelten heute möglicherweise nicht mehr.

0

Ich denke nicht unbedingt, instanceof ist schlecht für Programmierer, die wissen, was sie tun und es verwenden, um zu vermeiden, komplizierter Code zu schreiben, um es zu umgehen. Es gibt einen Nutzen für alles und auch einen Mißbrauch.

Mit diesem gesagt, erfordert die Beschreibung, die Sie bereitstellen, nicht instanceof. Es gibt verschiedene Möglichkeiten, dies ohne instanceof zu implementieren, und (die wichtigste Sache) muss die alternative Lösung besser sein als instanceof. Gehen Sie nicht einfach mit einer nicht-instanceof Lösung, um instanceof zu vermeiden, weil Sie gehört haben, dass es schlecht ist.

Ich denke, dass eine nicht instanceof Lösung für Ihr Szenario Vorteile hat, die Lösung einfacher erweiterbar zu machen.

for (Item i: items) { 
    if (ItemTypes.WEAPON_ITEM.equals(i.getType)) { 
     processWeaponType(i); 
    } 

} 

oder

for (Item i: items) { 
    if (WeaponItem.class.equals(i.getType)) { 
     processWeaponType(i); 
    } 

} 
+0

gut, aber Ihre Lösung ist nicht lesbarer als 'instanceof'. Leistungsmäßig ist 'instanceof' wirklich schnell; es ist definitiv schneller als unser eigenes 'type'-Feld zu vergleichen; es kann sogar schneller sein als der Vergleich von 'Class' mit' == '. – ZhongYu

+0

Vielleicht, aber ist Geschwindigkeit das gewünschte Ergebnis oder Software-Design und Architektur in Bezug auf diese Frage? Ich meine, wir können die Klassen alle zusammen loswerden und einfach alles auf eine lange Methode stellen, wenn Sie wirklich denken, dass der Unterschied in der Leistung für diese Frage eine Überlegung wert ist. Da der JIT-Compiler Ihren Code sowieso neu organisiert, ist es schwer zu sagen, welcher Code schneller ist. –

+0

Dies ist nicht besser als instanceof, seine a * Verkleidung * zum Beispiel. Keines dieser Vorschläge löst das Problem, dass beim Hinzufügen eines neuen Kindtyps der gesamte Client-Code geprüft/aktualisiert werden muss. – Durandal