2009-04-27 5 views
1

Ich habe eine Klasse, die eine Nachricht lesen sollte, extrahieren Sie ihren Typ aus dem Umschlag und rufen Sie dann eine Methode, die die Nachricht verarbeiten müssen; Jedem Nachrichtentyp ist ein Aufzählungsmember zugeordnet.Overhead der Suche und Aufruf einer Methode mit Reflektion

Es gibt mehr als 30 Nachrichtentypen.

Ein Weg, um die Nachricht zu versenden ist, nur einen Schalter zu verwenden, aber es ist wirklich hässlich und fehleranfällig (habe ich diesen Fall schon? Trace diesen Fall zweimal?). Eine andere Möglichkeit besteht darin, eine Schnittstelle mit einem einzelnen Methodenprozess (Daten) zu definieren und für jeden Nachrichtentyp, der diese Schnittstelle implementiert, eine Klasse zu erstellen, diese Klassen mit einer Karte zu registrieren und im Code, der die Nachricht verarbeiten soll, zu agieren. Rufen Sie einfach map.get (messageType) .process (data) auf; aber es ist wirklich ärgerlich, 30+ Klassen zu erstellen.

Ein anderer Weg ist die Verwendung von reflection: Definieren Sie für jeden Nachrichtentyp eine Funktion mit einem genauen Signatur- und Musternamen wie processMessagetype; dann erstellen Sie eine Karte von Nachrichtentyp zu Methode (gefüllt mit einer einfachen Suche auf getDeckaredMethods()) und dann etwas tun wie: map.get (messageType) .invoke (this, data).

Welche Methode sollten Sie verwenden? Was ist der Overhead der Java-Reflektion?

Antwort

4

Reflection hat einen großen Overhead, wenn Sie nach einer guten Performance suchen. Wenn Ihre Routine (Nachrichtenversand) immer wieder verwendet wird, ist die Alternative zum Polymorphismus besser. Auch IMO ist es besser, wenn man in einer OO-Perspektive schaut, denn wie von Brian auf seine Antwort angegeben, ist es einfacher und billiger für die Wartung.

Ihre Klassen, die 30+ Nachrichten bearbeiten, können leicht zu 100+ Nachrichten wachsen, dann haben Sie eine Klasse mit über 100 Methoden, fügen Sie auch die privaten Methoden hinzu und die Wiederverwendung von Code wird hart sein und Ihre Klasse ist gerade zu einem Chaos geworden.

Der Aufwand der Reflexion über Polymorphismus und Schalter ist so etwas wie dieses:

time spent on a switch case call: 1 
time spent on a polymorphism call: 1.1 
time spent on a reflection call: 1.9-2.0 

ich diese Jungs vor einer Weile gebenchmarkt, und jene, meine Ergebnisse waren, habe ich den Code jetzt nicht haben, weil diese Benchmarks waren getan bei meiner vorherigen Firma und ich habe keinen Zugriff mehr auf diesen Code.

+0

Großartig, das war genau das, was ich gesucht habe. Vielen Dank! – akappa

1

Sie sagen nicht, was Ihre process() -Methode tun wird, aber ich wäre überrascht, wenn ein Overhead beim Absetzen eines Methodenaufrufs überhaupt von Bedeutung wäre.

Ich würde die Sprache und OO-System für Sie arbeiten lassen, und erstellen Sie eine Schnittstelle/abstrakte Basisklasse mit der entsprechenden Methode Implementierung (en).

dass vorzeitige Optimierung Denken Sie daran, ist die Wurzel aller Übel (Knuth)

0

Ich würde denken, dass die erste Methode/Algorithmus Du beschrieben (eine Karte von Nachrichtentyp Klasse) wäre einfacher, im Laufe der Zeit zu verwalten mit Updates , Wartung usw.

Das zweite Muster, das Sie beschreiben, würde grundsätzlich eine Klasse mit ungefähr 30+ Methoden beinhalten, richtig? Das wäre im Laufe der Zeit ziemlich mühsam.

Benötigt jeder Nachrichtentyp eine wirklich eindeutige Verarbeitungslogik? Wenn Sie Implementierungen einer Schnittstelle zur Verarbeitung der Nachricht erstellen, könnten Sie wahrscheinlich von einer Typhierarchie profitieren, dh einer Oberklasse, die eine bestimmte Familie von Nachrichten analysieren kann, und Sie müssen sie dann nur in Bereichen überschreiben, in denen Nachrichten dieser Familie unterschiedlich sind. ..

+0

Guter Punkt über das Problem der Wiederverwendbarkeit von Code der verschiedenen Methoden. Ja, die Logik unterscheidet sich für jede Funktion, die wirklich klein ist (typischerweise benachrichtigen sie nur einige Klassen des Ereignisses, das der Nachricht zugeordnet ist, und nichts mehr). Der kleine Faktor der verschiedenen Funktionen (2-3 Zeilen Code) lässt mich über Reflexion nachdenken; Ansonsten werde ich sicher mit der Interface-Lösung gehen. – akappa

2

Sie könnten die Prozessmethode zur Enumeration hinzufügen.

Was genau ist falsch mit einem großen Schalter (mit jedem Fall nur eine Methode aufrufen)?

Die Hauptkosten der Reflexion ist, dass es dazu neigt, Code zu saugen. ;)

+0

+1 für den Hinweis auf die realen Kosten der Reflexion – dfa

1

Ich werde raten, da Sie über "Nachrichten" und "Umschläge" sprechen, dass Sie diese Nachrichten über den Draht übergeben. Die Zeit, die dafür benötigt wird, wird dominieren die zusätzliche Zeit, die es für die Reflexion braucht. Ich habe solche Überlegungen benutzt und es ist kein schlechtes Modell.

+0

Ja, aber ich verwende asynchrone E/A, daher sollte ein einzelner Thread Nachrichten verarbeiten, die von einem potenziell großen Satz von Verbindungen stammen, also mit einer guten Häufigkeit. – akappa

+0

Asynchrone E/A wird definitiv die Anzahl der Nachrichten erhöhen, die Sie verarbeiten können, aber es wird nichts zur Verbesserung der Leistung der Verarbeitung von * einer * Nachricht beitragen. Wie ich schon sagte, wird das wahrscheinlich von der Zeit bestimmt werden, die damit verbracht wurde, die Nachricht über die Leitung zu senden oder die Nachricht zu "verarbeiten". Reflexion wird keinen Unterschied machen. :-) – GuyBehindtheGuy

+0

Mmh, der Punkt ist, dass mit Asynchronous I/O, Schreib-und Leseoperationen nicht blockiert, also sollte ich nicht diese Zeiten in der Gleichung berücksichtigen. Fühlen Sie sich frei, mich zu korrigieren, wenn diese Argumentation fehlerhaft ist;) – akappa

Verwandte Themen