3 Stimmen

Wie man Java-Schnittstellen mit mehreren implementierenden Klassen verwendet

public interface Foo {
}

public class SpecificFoo implements Foo {
}

public interface SomeInterface {
    void thisMethod(Foo someKindOfFoo);
}

public class SomeClass implements SomeInterface {

    public void thisMethod(Foo someKindOfFoo) {
        // calling code goes into this function
         System.out.println("Dont go here please");
    }

    public void thisMethod(SpecificFoo specificFoo) {
        // not into this function
         System.out.println("Go here please");
    }
}

public class SomeOlderClass {

    public SomeOlderClass( SomeInterface inInterface ) {
        SpecificFoo myFoo = new SpecificFoo();

        inInterface.thisMethod(myFoo);
    }
}

Rufnummer:

SomeClass myClass = new SomeClass();
SomeOlderClass olderClass = new SomeOlderClass(myClass);

Ich habe eine Schnittstelle ( SomeInterface ), die mehrere Klassen aufrufen (z. B. SomeOlderClass ). Ich habe eine Klasse, die die Schnittstelle implementiert, aber ich möchte typsichere Operationen an den spezifischen Implementierungen durchführen, die an die generische Schnittstelle übergeben werden.

Wie im obigen Code gezeigt, möchte ich eigentlich eine weitere Methode erstellen, die dem spezifischen Typ entspricht, der an die Schnittstelle übergeben wird. Das funktioniert aber nicht. Ich nehme an, dass dies daran liegt, dass der aufrufende Code nur die Schnittstelle kennt, nicht aber die Implementierung mit den spezifischeren Methoden (auch wenn SpecificFoo implements Foo )

Wie kann ich dies also auf die eleganteste Weise tun? Ich kann den Code zum Laufen bringen, indem ich eine if-Anweisung in die Klasse einfüge, die die Schnittstelle implementiert ( SomeClass ) :

public void thisMethod(Foo someKindOfFoo) {
    // calling code goes into this function
    if ( someKindOfFoo.getClass().equals(SpecificFoo.class) )
        thisMethod(SpecificFoo.class.cast(someKindOfFoo));
    else
        System.out.println("Dont go here please");
}

Dies ist jedoch nicht elegant, da ich jedes Mal, wenn ich eine neue Art von Foo hinzufüge, if-Anweisungen hinzufügen muss. Und das könnte ich vergessen.

Die andere Möglichkeit besteht darin, Folgendes hinzuzufügen SpecificFoo zum SomeInterface und lassen Sie den Compiler mich daran erinnern, dass ich Implementierungen in SomeClass . Das Problem dabei ist, dass ich am Ende eine ganze Menge Code hinzufügen muss. (Wenn jemand anderes die Schnittstelle implementiert, muss er auch die neue Methode sowie alle Tests implementieren).

Es scheint eine weitere Option zu geben, die ich vermisse, da Foo y SpecificFoo verwandt sind. Ideen?

MEHR INFO:

Ich habe tatsächlich eine Weile daran gearbeitet, die Frage zu vereinfachen. Wenn ich mehr Details hinzufüge, steigt die Komplexität um einiges. Aber was soll's... Ich glaube, ich kann es erklären.

Grundsätzlich bin ich schreiben ein GWT Web-Anwendungen RPC Servlet mit dem Befehl Muster, wie von Ray Ryan in seinem erklärt sprechen

Es gibt mehrere Implementierungen davon auf Google Code, aber viele von ihnen leiden unter diesem inhärenten Problem. Ich dachte, es war ein Fehler in der GWT-RPC-Code Bugreport WIE auch immer, als ich weiter implementierte, bemerkte ich ein ähnliches Problem rein auf der Client-Seite und im gehosteten Modus. (dh alle java, keine gwt javascript Wahnsinn).

Also abstrahierte ich die grundlegenden Ideen zu einer rohen Java-Befehlszeile Fall, und sah das gleiche Problem, wie oben beschrieben.

Wenn Sie den Ausführungen von Ray Ryan folgen, Foo ist eine Aktion, SpecificFoo ist eine bestimmte Aktion, die ich aufrufen möchte. SomeInterface ist der clientseitige RPC-Dienst und SomeClass ist die serverseitige RPC-Klasse. SomeOlderClass ist eine Art rpc-Dienst, der sich mit Cacheing und dergleichen auskennt.

Offensichtlich, oder? Nun, wie ich schon sagte, denke ich, dass der ganze GWT RPC Unsinn nur das Wasser auf der Basis Frage verwirrt, weshalb ich versucht habe, es so gut ich konnte zu vereinfachen.

3voto

Esko Luontola Punkte 71758

Wenn Sie den tatsächlichen Typ eines Objekts zur Laufzeit herausfinden müssen, dann ist der Entwurf höchstwahrscheinlich falsch. Das verstößt zumindest gegen die Grundsatz der offenen Geschlossenheit und Grundsatz der Umkehrung der Abhängigkeiten .

(Da Java nicht über Mehrfachversand die thisMethod(Foo) wird aufgerufen anstelle von thisMethod(SpecificFoo) . Doppelter Versand könnte verwendet werden, um die Beschränkungen der Sprache zu umgehen, aber es könnte immer noch ein Designproblem darin lauern...)

Bitte geben Sie mehr Informationen darüber, was Sie zu erreichen versuchen. Im Moment bietet die Frage nicht genügend Informationen, um einen richtigen Entwurf zu erstellen.

Eine allgemeine Lösung ist, dass, da die Aktion vom Laufzeittyp von Foo sollte diese Methode Teil sein von Foo so dass seine Umsetzung je nach Foo Typ. Ihr Beispiel würde also in etwas wie unten geändert werden (möglicherweise durch Hinzufügen von SomeInterface oder andere Parameter an thisMethod() ).

public interface Foo {
    void thisMethod();
}

public class SpecificFoo implements Foo {
        public void thisMethod() {
                 System.out.println("Go here please");
        }
}

2voto

Philipp Punkte 45643

Versuchen Sie es mit Doppelversand: Fügen Sie eine Methode zur Foo Schnittstelle, die aufgerufen wird von SomeClass#thisMethod . Dann platzieren Sie den Code in der Implementierung dieser Methode.

public interface Foo {
  public void thatMethod(SomeClass a);
  public void thatMethod(SomeOlderClass a);
}

public class SomeClass implements SomeInterface {
    public void thisMethod(Foo someKindOfFoo) {
        someKindOfFoo.thatMethod(this);
    }
}

2voto

SingleShot Punkte 18270

Sorry, ich finde die Problembeschreibung viel zu abstrakt, um eine Empfehlung aussprechen zu können. Es handelt sich eindeutig um ein Designproblem, denn normalerweise sollte es nicht nötig sein, den Typ der Schnittstelle zu überprüfen. Ich werde es trotzdem versuchen... Zunächst muss ich Ihr Problem konkreter formulieren, damit mein kleines Gehirn es verstehen kann. Wie wäre es statt Foos mit Birds?

public interface Bird {
}

public class Ostrich implements Bird {
}

public interface BirdManager {
    void fly(Bird bird);
}

public class AdvancedBirdManager implements BirdManager {

    public void fly(Bird bird) {
        System.out.println("I am in the air. Yay!");
    }

    public void fly(Ostrich ostrich) {
        System.out.println("Sigh... I can't fly.");
    }
}

public class ZooSimulation {
    public ZooSimulation(BirdManager birdManager) {
        Ostrich ostrich = new Ostrich();
        birdManager.fly(ostrich);
    }
}

public static void main(String[] args) {
    AdvancedBirdManager advancedBirdManager = new AdvancedBirdManager();
    ZooSimulation zooSimulation = new ZooSimulation(advancedBirdManager);
}

Hier wird der Strauß verkünden: "Ich bin in der Luft. Juhu!", was nicht das ist, was wir wollen.

OK, also, ignorieren die Tatsache, dass ich hier grundlegende OO versagen, das Problem ist, dass die BirdManager wird für die am wenigsten spezifische Methode, die den Typ, der in übergeben wird suchen. Egal also, welche Art von Vogel ich ihm gebe, er wird immer passen fly(Bird) . Wir können einige if aber je mehr Vogelarten Sie hinzufügen, desto schlechter wird Ihr Design. Jetzt kommt der schwierige Teil - ich habe keine Ahnung, ob das im Zusammenhang mit Ihrem Problem Sinn macht, aber betrachten Sie dieses Refactoring, bei dem ich die Logik vom Manager in den Vogel verschiebe:

public interface Bird {
    void fly();
}

public class BasicBird implements Bird {
    public void fly() {
        System.out.println("I am in the air. Yay!");
    }
}

public class Ostrich implements Bird {
    public void fly() {
        System.out.println("Sigh... I can't fly.");
    }
}

public interface BirdManager {
    void fly(Bird bird);
}

public class AdvancedBirdManager implements BirdManager {

    public void fly(Bird bird) {
        bird.fly();
    }
}

public class ZooSimulation {
    public ZooSimulation(BirdManager birdManager) {
        Ostrich ostrich = new Ostrich();
        birdManager.fly(ostrich);
    }
}

public static void main(String[] args) {
    AdvancedBirdManager advancedBirdManager = new AdvancedBirdManager();
    ZooSimulation zooSimulation = new ZooSimulation(advancedBirdManager);
}

Unser Strauß sagt jetzt das Richtige, und der Vogelmanager behandelt ihn immer noch wie einen Vogel. Nochmals, schlechtes OO (Strauße sollten keine fly() Methoden), aber es veranschaulicht meine Gedanken.

0voto

finnw Punkte 46519

Solange es nicht zu viele Implementierungen von Foo würde ich eine abstrakte Methode in SomeInterface für jede Unterklasse von Foo und eine abstrakte Klasse Aufrufe an eine Standardmethode weiterleiten lassen, die für den allgemeinsten Typ definiert ist:

public interface Foo {
}

public class SpecificFoo implements Foo {
}

public interface SomeInterface {
        void thisMethod(Foo someKindOfFoo);
        void thisMethod(SpecificFoo specificFoo);
        void thisMethod(OtherSpecificFoo otherSpecificFoo);
}

public abstract class AbstractSomeInterface {
        public void thisMethod(Foo wrongFoo) {
            throw new IllegalArgumentException("Wrong kind of Foo!");
        }

        public void thisMethod(SpecificFoo specificFoo) {
            this.thisMethod((Foo) specificFoo);
        }

        public void thisMethod(OtherSpecificFoo otherSpecificFoo) {
            this.thisMethod((Foo) specificFoo);
        }
}

public class SomeClass extends AbstractSomeInterface {
        public void thisMethod(SpecificFoo specificFoo) {
                 // calling code goes into this function
                 System.out.println("Go here please");
        }
}

public class SomeOlderClass {

        public SomeOlderClass( SomeInterface inInterface ) {
                SpecificFoo myFoo = new SpecificFoo();

                inInterface.thisMethod(myFoo);
        }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X