Methoden reflektiv aufrufen oder feste Methoden mit Vererbung verwenden?

8

Ich arbeite an einer kleinen Web-Bibliothek und frage mich, ob ich die HTTP-Handler-Methoden für GET, POST, PUT etc. reflectivly oder nicht aufrufen sollte.

Feste Methoden

Zuerst die Variante mit einem if else ... block Aufrufmethoden in der Basisklasse, wo sie eine Standardimplementierung haben, die einen Fehler an den Client zurückgibt. Da eine Anfrage an eine nicht unterstützte Methode einen Header mit den erlaubten Methoden benötigt, muss ich reflektorisch nachschlagen, welche Methoden actully überschrieben werden (wie übrigens die Servlet-API).

%Vor%

Der Nachteil dieser Lösung ist die reduzierte Flexibilität, da die verwendbaren Methoden in der Basisklasse festgelegt sind, bis die Methode handle in einer Unterklasse neu implementiert wird.

Variable Methoden

Die andere Variante besteht darin, HTTP-Handler-Methoden abhängig von ihrer Signatur nachzuschlagen (nehmen Sie HttpServletRequest und return Response ). Diese Methoden würden in einer Map gespeichert und reflektorisch abhängig vom Schlüssel in der Map aufgerufen.

%Vor%

Der Vorteil dieser Lösung ist die einfache Implementierung und Flexibilität, aber die Nachteile sind vielleicht ein bisschen mehr Laufzeitaufwand aufgrund der Suche in der Karte und der reflektive Methodenaufruf. Und die Schnittstelle der Klasse ist etwas "weich" (oder dynamisch) und der Compiler hat keine Chance, es zu überprüfen. Aber ich bin nicht sicher, ob dies ein Nachteil wäre, da keine anderen Klassen auf die HTTP-Handler-Methoden zurückgreifen sollten, sie sind die externe Web-Schnittstelle und der Rand des Java-Systems.

Strategie Muster

Die dritte Option und die sauberste OOP wäre das Strategie-Muster, wie es von "Polygene Schmierstoffen" empfohlen wird. Es würde so aussehen:

%Vor%

Es ist sauber OOP, aber der Code ist wirklich hässlich und ausführlich. Ich würde Scala lieber mit Verschlüssen hier vorziehen, obwohl die Werkzeugunterstützung für Scala immer noch schlecht ist. Vergleichen Sie dies mit der Lösung mit Vererbung und festen Methoden:

%Vor%

Was würden Sie bevorzugen und warum? Andere Ideen?

Lösung

Ich habe gelernt, dass hier aufgrund der festgelegten Menge von HTTP-Methoden keine Reflektion erforderlich ist. Die Herangehensweise mit dem Strategie-Muster ist sauber, aber es scheint mir sehr ausführlich zu sein. Also entschied ich mich für die festen Methoden und die Vererbung.

    
deamon 25.06.2010, 09:03
quelle

3 Antworten

9

Verwendung von Schnittstellen anstelle von Reflektion

Reflection sollte in diesem Fall nicht verwendet werden, besonders, da es nicht notwendig ist, mit zu beginnen (siehe Effektive Java 2nd Edition, Item 53: Interfaces zur Reflektion bevorzugen ).

Anstatt java.lang.reflect.Method zu verwenden und a Map<String, Method> handlers , Sie sollten einen interface RequestHandler Typ definieren und stattdessen einen Map<String, RequestHandler> handlers haben.

Es würde ungefähr so ​​aussehen:

%Vor%

Dann füllen Sie die Karte mit einem expliziten put aus (oder verwenden Sie vielleicht eine Konfigurationsdatei usw.), anstatt nach Handlern zu suchen. Dann können Sie statt reflectively Method.invoke sauberer einfach RequestHandler.handle aufrufen.

On enum Schlüssel Option

Wenn Sie nur ein paar verschiedene Typen von Request-Methoden haben, die nicht erweiterbar sind, können Sie eine enum RequestMethod { GET, POST; } haben.

Damit können Sie Map<RequestMethod, RequestHandler> handlers; deklarieren. Denken Sie daran, dass enum eine valueOf(String) -Methode hat, mit der Sie die Konstante aus dem Namen abrufen können.

Bei Verwendung von interface vs abstract class

Hier werde ich noch einmal auf das Urteil von Josh Bloch aus Effektives Java 2., Punkt 18 verweisen: Bevorzugen Sie Interfaces zu abstrakten Klassen :

  

Zusammenfassend ist ein interface im Allgemeinen der beste Weg, einen Typ zu definieren, der mehrere Implementierungen zulässt. Eine Ausnahme von dieser Regel ist der Fall, in dem die Leichtigkeit der Evolution wichtiger ist als Flexibilität und Macht. Unter diesen Umständen sollten Sie einen abstract class verwenden, um den Typ zu definieren, aber nur, wenn Sie die Einschränkungen verstehen und akzeptieren können.

Die Probleme, mit denen Sie zu kämpfen haben, wurden ausführlich im Buch behandelt. In diesem speziellen Fall kann für die Verwendung eines abstract class (d. H. Der "Methode mit fester Methode") wegen der wenigen und festen Arten von Anforderungsmethoden da draußen gelten.

    
polygenelubricants 25.06.2010, 09:08
quelle
7

Ich würde es vorziehen, hier keine Reflexion zu verwenden, da die möglichen HTTP-Methoden bekannt sind und sich in absehbarer Zeit nicht ändern werden. So gewinnen Sie für die zusätzliche Komplexität und das Fehlen von Laufzeitprüfungen nicht wirklich etwas. Anstelle von if...elseif s könnten Sie jedoch eine Map verwenden, um Ihren Code sauberer und einfacher erweiterbar zu machen.

Gegenwärtig würde Ihr reflektierender Code abstürzen, wenn er mit einem Methodennamen wie "NOSUCHMETHOD" aufgerufen wird.

    
Péter Török 25.06.2010 09:08
quelle
1

Ein gutes OO-Design sollte die Erweiterbarkeit unterstützen, wenn es erforderlich ist. Hier sind ein paar Gedanken darüber, was Erweiterbarkeit (für mich) bedeutet, die ich mit dir teile.

Stufe 1: keine Erweiterbarkeit

Alles ist im Voraus bekannt, Sie können es so programmieren, dass es am einfachsten und verständlich wird.

Ebene 2: gekapselte Erweiterbarkeit mit Hooks

Eine Komponente ist gut gekapselt, aber es muss möglich sein, ihr Verhalten anzupassen oder zu konfigurieren. Die Komponente bietet dann einen Haken, an den Sie Dinge anschließen können, um ihr Verhalten zu ändern. Der Haken kann jedoch viele verschiedene Formen annehmen. Dies ist zum Beispiel der Fall, wenn eine Komponente eine Möglichkeit bietet, eine Strategie einzubinden, oder wenn eine Karte mit <verb, actionHandler> intern von der Komponente verwendet wird, die Karte jedoch extern von einer anderen Komponente usw. gefüllt wird.

Level 3: Laufzeitverlängerung

Dinge sind nicht im fortgeschrittenen Wissen, Code wird dynamisch geladen, das System ist "skriptfähig" mit benutzerdefinierten Regeln, die in einem DSL etc. ausgedrückt werden. Dieser Grad an Flexibilität erfordert die Verwendung von Reflektion, weil ein Teil des Codes in einem aufgerufen werden muss dynamischer Weg.

In Ihrem Fall ist die Liste der HTTP-Verben fest und intern in der Ressource. Ich würde dann für die einfachste gehen und Ansatz 1 verwenden. Alles andere sieht für mich over-engineering aus und mir stört es ein wenig if-else Liste, wenn es gerechtfertigt ist.

    
ewernli 25.06.2010 11:44
quelle

Tags und Links