Polymorphismus, wie vermeidet man Typcasting?

8

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 :

%Vor%

und Response ist wie folgt:

%Vor%

Die obige Lösung hat zwei Probleme:

  1. Die execute -Methode ist voll von if , else if blocks.
  2. Es könnte sein, dass wenn eine falsche Antwort zurückgegeben wird, z.B. eine, in der 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.

%Vor%

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:

%Vor%

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?

    
insumity 01.10.2014, 22:38
quelle

9 Antworten

7

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:

%Vor%

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.

    
OldCurmudgeon 01.10.2014 22:51
quelle
4

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:

  • jede Antwort ist an ihre Anfrage gebunden, weil die Anfrage die einzige Möglichkeit ist, die Antwort zu erhalten
  • Sie können auf Anfragen und Antworten über ihre gemeinsame Schnittstelle zugreifen (oder eine abstrakte Klasse erstellen, wenn Sie die Funktionalität teilen möchten).
  • Jede Anfrage und Antwort kann ihre spezifischen Eingabe- und Ausgabeparameter haben (mehr als einmal)
  • Sie können die Parameter typsicher aufrufen

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:

  • Jede Anfrage / Antwort hat andere Parameter (und nicht nur eine)
  • Sie möchten anstelle ihrer eingerahmten Varianten einfache Datentypen verwenden
  • Der Code zum Senden / Abrufen ist nicht so einheitlich, dass nur die Datentypbehandlung für die Spezialisierungen unterschiedlich ist.

Eine Spielzeugimplementierung in Groovy (Java auf Steroiden), die die Internet Chuck Norris Datenbank :

%Vor%     
ChrLipp 06.10.2014 17:26
quelle
1

Wenn Sie Generika verwenden, können Sie Folgendes tun:

%Vor%

Es beinhaltet immer noch das Casting auf T , aber zumindest tust du es in den Tiefen einer Methode, anstatt ständig nach Klassenart und Rückgabewerten zu suchen.

    
SergeyB 09.10.2014 21:19
quelle
1

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:

%Vor%     
Christian Strempfer 08.10.2014 21:43
quelle
0

Vielleicht wäre ein mehr OO-Ansatz:

%Vor%

Auf diese Weise können Sie Folgendes verwenden:

%Vor%     
Muli Yulzary 05.10.2014 09:31
quelle
0

Jedes Type hat einen spezifischen Inhaltstyp der Antwort. Dies sollte im Code dargestellt werden und kann mit Generics erfolgen.

%Vor%

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.

%Vor%

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.

%Vor%

Die Request kann mithilfe von Polymorphismus implementiert werden.

%Vor%

Die Instanz der Anfragen kann in Type verschoben werden.

%Vor%

Hier sind die resultierenden Methodenaufrufe.

%Vor%     
CKuck 09.10.2014 14:53
quelle
-1

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%     
faheem farhan 09.10.2014 07:41
quelle
-1

Wie wäre es damit?

%Vor%

Ich habe string als Antwort verwendet, da integer ein Argument erwartet.

    
Ravindra HV 09.10.2014 19:02
quelle
-1

halte es einfach:

%Vor%     
user2504380 09.10.2014 20:20
quelle