Eine Kette von "Instanz von" Operationen wird als "Codegeruch" betrachtet. Die Standardantwort lautet "Polymorphismus verwenden". Wie würde ich das in diesem Fall machen?
Es gibt eine Reihe von Unterklassen einer Basisklasse. Keiner von ihnen ist unter meiner Kontrolle. Eine analoge Situation wäre mit den Java-Klassen Integer, Double, BigDecimal usw.
if (obj instanceof Integer) {NumberStuff.handle((Integer)obj);}
else if (obj instanceof BigDecimal) {BigDecimalStuff.handle((BigDecimal)obj);}
else if (obj instanceof Double) {DoubleStuff.handle((Double)obj);}
Ich habe die Kontrolle über NumberStuff und so weiter.
Ich möchte nicht viele Codezeilen verwenden, wo ein paar Zeilen reichen würden. (Manchmal mache ich eine HashMap, die Integer.class einer Instanz von IntegerStuff, BigDecimal.class einer Instanz von BigDecimalStuff usw. zuordnet. Aber heute möchte ich etwas Einfacheres.)
Ich möchte so etwas Einfaches:
public static handle(Integer num) { ... }
public static handle(BigDecimal num) { ... }
Aber Java funktioniert einfach nicht so.
Ich möchte beim Formatieren statische Methoden verwenden. Die Dinge, die ich formatiere, sind zusammengesetzt, wobei ein Thing1 ein Array Thing2s und ein Thing2 ein Array von Thing1s enthalten kann. Ich hatte ein Problem, als ich meine Formatierer wie folgt implementierte:
class Thing1Formatter {
private static Thing2Formatter thing2Formatter = new Thing2Formatter();
public format(Thing thing) {
thing2Formatter.format(thing.innerThing2);
}
}
class Thing2Formatter {
private static Thing1Formatter thing1Formatter = new Thing1Formatter();
public format(Thing2 thing) {
thing1Formatter.format(thing.innerThing1);
}
}
Ja, ich kenne die HashMap und ein bisschen mehr Code kann das auch beheben. Aber die "Instanz von" scheint im Vergleich so lesbar und wartbar zu sein. Gibt es etwas Einfaches, aber nicht stinkendes?
Anmerkung hinzugefügt 10.05.2010:
Es stellt sich heraus, dass wahrscheinlich in Zukunft neue Unterklassen hinzugefügt werden und mein vorhandener Code sie ordnungsgemäß behandeln muss. Die HashMap für Klasse funktioniert in diesem Fall nicht, da die Klasse nicht gefunden wird. Eine Kette von if-Anweisungen, die mit den spezifischsten beginnen und mit den allgemeinsten enden, ist wahrscheinlich die beste:
if (obj instanceof SubClass1) {
// Handle all the methods and properties of SubClass1
} else if (obj instanceof SubClass2) {
// Handle all the methods and properties of SubClass2
} else if (obj instanceof Interface3) {
// Unknown class but it implements Interface3
// so handle those methods and properties
} else if (obj instanceof Interface4) {
// likewise. May want to also handle case of
// object that implements both interfaces.
} else {
// New (unknown) subclass; do what I can with the base class
}
quelle
[text](link)
diese Option, um Links in Kommentaren zu veröffentlichen.Antworten:
Dieser Eintrag aus Steve Yegges Amazon-Blog könnte Sie interessieren: "Wenn der Polymorphismus versagt" . Im Wesentlichen spricht er Fälle wie diesen an, in denen Polymorphismus mehr Probleme verursacht als löst.
Das Problem ist, dass Sie zur Verwendung des Polymorphismus die Logik des "Handles" Teil jeder "Schalt" -Klasse machen müssen - in diesem Fall Integer usw. Dies ist eindeutig nicht praktikabel. Manchmal ist es logischerweise nicht einmal der richtige Ort, um den Code einzufügen. Er empfiehlt den "Instanz of" -Ansatz als das geringere von mehreren Übeln.
Wie in allen Fällen, in denen Sie gezwungen sind, stinkenden Code zu schreiben, lassen Sie ihn in einer Methode (oder höchstens einer Klasse) zugeknöpft, damit der Geruch nicht austritt.
quelle
instanceof
.Monster
Klasse eine Methode hinzufügt , deren Aufgabe im Wesentlichen darin besteht, das Objekt einzuführen, z. B. "Hallo, ich bin ein Ork. Was denkst du über mich?". Der meinungsgebundene Elf kann dann Monster anhand dieser "Grüße" mit einem ähnlichen Code beurteilenbool visitOrc(Orc orc) { return orc.stench()<threshold; } bool visitFlower(Flower flower) { return flower.colour==magenta; }
. Der einzige monsterspezifische Code ist dannclass Orc { <T> T accept(MonsterVisitor<T> v) { v.visitOrc(this); } }
genug für jede Monsterinspektion ein für alle Mal.Wie in den Kommentaren hervorgehoben, wäre das Besuchermuster eine gute Wahl. Ohne direkte Kontrolle über das Ziel / den Akzeptor / den Visitee können Sie dieses Muster jedoch nicht implementieren. Hier ist eine Möglichkeit, wie das Besuchermuster hier möglicherweise noch verwendet werden kann, obwohl Sie keine direkte Kontrolle über die Unterklassen haben, indem Sie Wrapper verwenden (am Beispiel Integer):
Natürlich kann das Einpacken einer Abschlussklasse als Eigengeruch angesehen werden, aber vielleicht passt es gut zu Ihren Unterklassen. Persönlich denke ich nicht,
instanceof
dass es hier so schlimm riecht, besonders wenn es auf eine Methode beschränkt ist und ich es gerne verwenden würde (wahrscheinlich über meinen eigenen Vorschlag oben). Wie Sie sagen, es ist gut lesbar, typsicher und wartbar. Wie immer, halte es einfach.quelle
instanceof
die richtigehandle()
Methode aufzurufen , müssen Sie jetzt den richtigenXWrapper
Konstruktor aufrufen ...Anstelle eines riesigen
if
können Sie die von Ihnen behandelten Instanzen in eine Map einfügen (Schlüssel: Klasse, Wert: Handler).Wenn die Suche nach Schlüssel zurückgegeben wird
null
, rufen Sie eine spezielle Handlermethode auf, die versucht, einen passenden Handler zu finden (z. B. indem SieisInstance()
jeden Schlüssel in der Karte aufrufen ).Wenn ein Handler gefunden wird, registrieren Sie ihn unter dem neuen Schlüssel.
Dies macht den allgemeinen Fall schnell und einfach und ermöglicht es Ihnen, die Vererbung zu handhaben.
quelle
Sie können Reflexion verwenden:
Sie können die Idee erweitern, Unterklassen und Klassen, die bestimmte Schnittstellen implementieren, generisch zu behandeln.
quelle
if then else
Kette auseinandersetzen, um Handler hinzuzufügen, zu entfernen oder zu ändern. Der Code ist weniger anfällig für Änderungen. Aus diesem Grund würde ich sagen, dass es deminstanceof
Ansatz überlegen ist . Jedenfalls wollte ich nur eine gültige Alternative geben.getMethod(String name, Class<?>... parameterTypes)
? Sonst würde ich ersetzen==
mitisAssignableFrom
für die Typprüfung des Parameters.Sie können das Muster der Verantwortungskette berücksichtigen . Für Ihr erstes Beispiel so etwas wie:
und dann ähnlich für Ihre anderen Handler. Dann müssen die StuffHandler der Reihe nach aneinander gereiht werden (am spezifischsten bis am wenigsten spezifisch, mit einem endgültigen 'Fallback'-Handler), und Ihr Despatcher-Code ist gerecht
firstHandler.handle(o);
.(Eine Alternative besteht darin, anstatt eine Kette zu verwenden, nur eine
List<StuffHandler>
in Ihrer Dispatcher-Klasse zu haben und sie die Liste durchlaufen zu lassen, bishandle()
true zurückgegeben wird.)quelle
Ich denke, dass die beste Lösung HashMap mit Class als Schlüssel und Handler als Wert ist. Beachten Sie, dass die HashMap-basierte Lösung in der konstanten algorithmischen Komplexität θ (1) ausgeführt wird, während die Geruchskette von if-instanceof-else in linearer algorithmischer Komplexität O (N) ausgeführt wird, wobei N die Anzahl der Glieder in der if-instanceof-else-Kette ist (dh die Anzahl der verschiedenen zu behandelnden Klassen). Daher ist die Leistung einer HashMap-basierten Lösung asymptotisch N-mal höher als die Leistung einer if-instanceof-else-Kettenlösung. Beachten Sie, dass Sie verschiedene Nachkommen der Nachrichtenklasse unterschiedlich behandeln müssen: Nachricht1, Nachricht2 usw. Unten finden Sie das Code-Snippet für die HashMap-basierte Behandlung.
Weitere Informationen zur Verwendung von Variablen vom Typ Class in Java: http://docs.oracle.com/javase/tutorial/reflect/class/classNew.html
quelle
Gehen Sie einfach mit der Instanz von. Alle Problemumgehungen scheinen komplizierter zu sein. Hier ist ein Blog-Beitrag, der darüber spricht: http://www.velocityreviews.com/forums/t302491-instanceof-not-always-bad-the-instanceof-myth.html
quelle
Ich habe dieses Problem mit
reflection
(vor etwa 15 Jahren in der Zeit vor Generika) gelöst .Ich habe eine generische Klasse (abstrakte Basisklasse) definiert. Ich habe viele konkrete Implementierungen der Basisklasse definiert. Jede konkrete Klasse wird mit className als Parameter geladen. Dieser Klassenname wird als Teil der Konfiguration definiert.
Die Basisklasse definiert den gemeinsamen Status für alle konkreten Klassen, und konkrete Klassen ändern den Status, indem sie die in der Basisklasse definierten abstrakten Regeln überschreiben.
Zu diesem Zeitpunkt kenne ich den Namen dieses Mechanismus nicht, der als bekannt ist
reflection
.In diesem Artikel sind nur wenige weitere Alternativen aufgeführt :
Map
undenum
abgesehen von der Reflexion.quelle
GenericClass
eininterface
?