Es tut mir leid für die lange Frage, aber ertragen Sie mit mir, ich habe versucht, mein Problem so verständlich wie möglich zu machen. Wenn Sie glauben, dass es präziser sein kann, können Sie es bearbeiten.
Ich habe ein Client-Server-System, bei dem der Client verschiedene Arten von Anfragen an den Server sendet und auf der Grundlage der Anfrage eine Antwort erhält.
Der Code im Client-System lautet:
%Vor% Damit der obige Code korrekt funktioniert, ist die Klasse Request
:
und Response
ist wie folgt:
Die obige Lösung hat zwei Probleme:
execute
-Methode ist voll von if
, else if
blocks. someString
nicht initialisiert ist, z.B. es wurde mit der Antwort für die Anfrage von Typ A verwechselt. Über das erste Problem habe ich eine Lösung mit Polymorphie gefunden. Also haben Sie eine Elternklasse Request
und für jede Art von Anfrage haben Sie eine Unterklasse von Request
, also haben Sie eine RequestTypeA
und RequestTypeB
. Alle Klassen überschreiben die Methode execute
.
Über das 2. Problem Ich habe nur eine mögliche Idee, wie es zu lösen ist: Ähnlich wie Request
erstelle Unterklassen von Response
basierend auf der Antwort und habe so etwas.
Jetzt kann ich sicher sein, dass eine Antwort vom Typ ResponseTypeB
eine gültige Zeichenfolge enthält. Und ich kann den Client-Code wie folgt schreiben:
und jetzt muss ich den Return-Typ von execute
eintippen.
Meine Hauptfrage / mein Problem ist: Gibt es eine Möglichkeit, in diesem Fall das Casting zu vermeiden? Oder wenn Sie eine bessere Lösung (Entwurfsmuster?) Für das oben genannte Problem kennen?
Der Versuch, die Anfrage von der Antwort zu trennen, ist zwecklos. Sie sind durch die API verbunden - R r = f(Q)
.
Sie haben ein RequestA
, das ein int
und ein RequestB
zurückgibt, das ein String
zurückgibt. Sie könnten eindeutig etwas tun wie:
Eine ausgefeiltere Version könnte etwa so aussehen:
%Vor% Dies ergibt sich aus einer realen Verwendung von JSON
und Apache HttpClient
- jedoch funktioniert es möglicherweise nicht wie gepostet, da ich die meisten Fehlerbehandlungs- und Wiederholungsmechanismen der Einfachheit halber entfernt habe. Es ist hier vor allem, um die Verwendung des vorgeschlagenen Mechanismus zu demonstrieren.
Beachten Sie, dass, obwohl in diesem Code kein Casting enthalten ist (wie von der Frage gefordert), Casting hinter den Kulissen in rsp.readValueAs(r)
stattfindet, was mit JSON
nicht möglich ist.
Jeder Switch (oder if / else if / else), der auf Typen basiert, ist ein Zeichen für ein schlechtes OO-Design .
Wie OldCurmudgeon sagte: Jede Anfrage ist an ihre Antwort gebunden - eine Anfrage und eine Antwort sind ein Paar. Also würde ich genau das tun, was Sie in Ihrem Text vorschlagen, aber nicht in Ihrem Code implementiert haben:
Über das erste Problem habe ich eine Lösung mit Polymorphie gefunden. Haben Sie also eine übergeordnete Klasse Request und für jede Art von Anfrage eine Unterklasse von Request, so haben Sie eine RequestTypeA und RequestTypeB. Alle Klassen überschreiben die Ausführungsmethode. Die Basisklassen sehen also so aus:
%Vor%Beachten Sie, dass ich Request von einer konkreten Klasse in eine Schnittstelle geändert habe. Die konkrete Implementierung für A (mit kovarianten Rückgabetypen Ich vermeide die Notwendigkeit zum Casting) sieht folgendermaßen aus:
%Vor%Und die konkrete Umsetzung für B:
%Vor%Dieses Design stellt Folgendes sicher:
Anwendungsbeispiel:
%Vor%Eine Lösung mit Generika (präsentiert von OldCurmudgeon) ist auch in Ordnung. Verwenden Sie eine manuelle Implementierung aller Anfrage / Antwort-Paare und keine Generika, wenn:
Eine Spielzeugimplementierung in Groovy (Java auf Steroiden), die die Internet Chuck Norris Datenbank :
%Vor%Die anderen Antworten waren mit Generika auf dem richtigen Weg, aber sie sind zu kompliziert, da zusätzliche Klassen und redundante Deklarationen von Antworttypen erforderlich sind.
Es könnte so einfach sein wie:
%Vor%oder sogar
%Vor% Sie werden kein Casting benötigen und deshalb wird ClassCastException
s nicht ausgelöst, sondern Fehler kompiliert, wie es ohne Generics der Fall wäre.
Andere Beispiele:
%Vor%Warum kann ich die generische Typdeklaration für Variablen nicht überspringen?
%Vor% Wenn Sie den generischen Typ nicht explizit deklarieren, wird er von Java als Object
behandelt. Daher wird getResult()
ein Objekt zurückgeben. Da Java eine stark typisierte Sprache ist, dürfen Sie kein Objekt in eine Integer-Variable ohne Casting einfügen. Es gibt keine Problemumgehung dafür.
Der Antworttyp ist an die Anforderung gebunden, um bei der Verwendung eine Typdeklaration zu vermeiden. Wenn ein Anforderungstyp verschiedene Antworttypen zurückgeben kann, ist er wahrscheinlich nicht gut genug eingekapselt und Sie sollten ihn in zwei verschiedene Anforderungstypen aufteilen oder den Antworttyp neu gestalten.
Ich nehme an, Sie wissen bereits, wie Sie die HTTP-Antwort erhalten, deshalb habe ich diesen Teil übersprungen.
%Vor% P.S. Wenn Sie AbstractRequest
nicht ableiten möchten, könnten Sie es nicht-abstrakt machen und es direkt instanziieren. In diesem Fall könnten Sie den Diamant-Operator mit Java 7 und höher verwenden:
Vielleicht wäre ein mehr OO-Ansatz:
%Vor%Auf diese Weise können Sie Folgendes verwenden:
%Vor% Jedes Type
hat einen spezifischen Inhaltstyp der Antwort. Dies sollte im Code dargestellt werden und kann mit Generics erfolgen.
Die verschiedenen Typen können als Konstanten definiert werden.
%Vor%Die Instanziierung der Anfragen ändert sich nur minimal.
%Vor% Die Antwort kann so geändert werden, dass Type
für den Zugriff auf den Inhalt verwendet wird.
So ist Gießen nicht notwendig.
%Vor% Jede fehlende Übereinstimmung zwischen Type
und dem Inhaltstyp der Antwort wird von den meisten IDEs oder dem Compiler gemeldet.
Die Response
kann als generische Klasse implementiert werden.
Die Request
kann mithilfe von Polymorphismus implementiert werden.
Die Instanz der Anfragen kann in Type
verschoben werden.
Hier sind die resultierenden Methodenaufrufe.
%Vor%Ich habe Enum verwendet, um alle möglichen Rückgabetypen zu speichern.
%Vor%Definieren Sie Unterklassen für Anforderungs- und Antwortklassen.
Jede Unterklasse der Request-Klasse überschreibt ihre execute-Methode und gibt die entsprechende Unterklasseninstanz der Response zurück.
%Vor%Verwenden Sie das schließlich in Ihrer Aufrufmethode
%Vor%Wie wäre es damit?
%Vor%Ich habe string als Antwort verwendet, da integer ein Argument erwartet.
Tags und Links java design oop casting polymorphism