2009-10-02 7 views
3
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); 
    } 
} 

code appelant:Comment utiliser des interfaces Java avec plusieurs classes de mise en œuvre

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

J'ai une interface (SomeInterface) que plusieurs classes remettent en (comme SomeOlderClass). J'ai une classe qui implémente l'interface, mais je veux faire des opérations sécurisées de type sur les implémentations spécifiques qui sont passées dans l'interface générique.

Comme indiqué dans le code ci-dessus, je veux vraiment pouvoir faire une autre méthode qui correspond au type spécifique transmis à l'interface. Cela ne fonctionne pas. Je suppose que c'est parce que le code appelant ne connaît que sur l'interface, et non la mise en œuvre des méthodes plus spécifiques (même si SpecificFoo implements Foo)

Alors, comment puis-je faire cela de la manière la plus élégante? Je peux obtenir le code de travail en ajoutant une instruction if dans la classe qui implémente l'interface (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"); 
} 

Cependant, ce n'est pas élégant, que je dois ajouter, si des déclarations chaque fois que j'ajouter un nouveau type de Foo . Et je pourrais oublier de le faire.

L'autre option consiste à ajouter SpecificFoo au SomeInterface, et de laisser le compilateur trier rappelant que j'ai besoin d'implémentations dans SomeClass. Le problème avec ceci est que je finis par ajouter un peu de code de plaque de chaudière. (Si quelqu'un d'autre implémente l'interface, ils doivent implémenter la nouvelle méthode, ainsi que tous les tests)

Il semble qu'il devrait y avoir une autre option qui me manque, étant donné que Foo et SpecificFoo sont liés. Des idées?

PLUS D'INFO:

Eh bien, je effectivement travaillé pendant un certain temps pour essayer de simplifier la question. Comme j'ajoute plus de détails, la complexité augmente un peu. Mais peu importe ... je pense que je peux l'expliquer.

Fondamentalement, je suis écrire un web GWT applications servlet RPC en utilisant le modèle de commande comme expliqué par Ray Ryan dans son talk

Il y a plusieurs implémentations de celui-ci sur google code, mais beaucoup d'entre eux souffrent de ce problème de Hériter. Je pensais que c'était un bug dans le code GWT-RPC bugreport Cependant, comme je l'implémentais plus loin, j'ai remarqué un problème similaire qui se passe purement sur le côté client, et en mode hébergé. (c'est-à-dire tous les java, pas de folie gwt javascript). J'ai donc abstrait les idées de base dans un cas de ligne de commande java brut, et j'ai vu le même problème, comme décrit ci-dessus.

Si vous suivez ce que Ray Ryan décrit, Foo est une action, SpecificFoo est une action spécifique que je veux appeler. SomeInterface est le service RPC côté client et SomeClass est la classe RPC côté serveur. SomeOlderClass est un type de service rpc qui serait au courant de la mise en cache et autres joyeusetés.

Évident, non? Eh bien, comme je l'ai dit, je pense que toutes les absurdités du GWT RPC ne font que brouiller les cartes sur le problème de base, et c'est pourquoi j'ai essayé de le simplifier du mieux que je pouvais.

+1

Pourquoi le sang sort-il de mes oreilles? – Jherico

Répondre

3

Si vous avez besoin de connaître le type réel d'un objet lors de l'exécution, la conception est probablement erronée. Cela viole au moins le Open Closed Principle and Dependency Inversion Principle.

(Parce que Java n'a pas multiple dispatch, le thisMethod(Foo) sera appelé au lieu de thisMethod(SpecificFoo). Double dispatch pourrait être utilisé pour contourner les limites de la langue, mais il pourrait encore y avoir un problème de conception qui se cache là-bas ...)

S'il vous plaît donner plus d'informations sur ce que vous essayez d'accomplir. En ce moment, la question ne fournit pas suffisamment d'informations pour arriver à un bon design.

Une solution générique est que, puisque l'action dépend du type d'exécution de Foo, cette méthode devrait faire partie de Foo de sorte que sa mise en œuvre peut varier selon le type de Foo. Donc, votre exemple serait changé en quelque chose comme ci-dessous (en ajoutant éventuellement SomeInterface ou d'autres paramètres à thisMethod()).

public interface Foo { 
    void thisMethod(); 
} 

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

Essayez en utilisant le double expédition: Ajouter une méthode à l'interface Foo qui est appelé par SomeClass#thisMethod. Puis placez le code dans l'implémentation de cette méthode.

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); 
    } 
} 
2

Désolé, je trouve la description du problème bien trop abstraite pour pouvoir faire une recommandation. Vous avez clairement un problème de conception car vous ne devriez généralement pas avoir besoin de vérifier le type d'interface. Je vais essayer ... D'abord, je dois rendre votre problème plus concret pour que mon petit cerveau puisse le comprendre. Au lieu de Foos, que diriez-vous des oiseaux?

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); 
} 

Ici, l'autruche déclarera «Je suis dans l'air. ce qui n'est pas ce que nous voulons. OK, donc, en ignorant le fait que j'échoue ici OO de base, le problème est que le BirdManager cherchera la méthode la moins spécifique qui correspond au type qui est passé. Donc, peu importe quel genre d'oiseau je donnez-le, il correspondra toujours fly(Bird). Nous pouvons mettre quelques if contrôles là, mais comme vous ajoutez plus de types d'oiseaux, votre conception se dégradera plus loin. Voici la partie difficile - Je ne sais pas si cela est logique dans le contexte de votre problème, mais considère que ce refactoring où je déplace la logique du gestionnaire en oiseaux:

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); 
} 

Notre autruche dit maintenant la chose correcte et le gestionnaire des oiseaux le traite toujours comme un oiseau. Encore une fois, mauvais OO (Ostriches ne devrait pas avoir fly() méthodes) mais il illustre mes pensées.

+0

(voir mes additions ci-dessus). Note à l'attention de soi-même: utiliser des oiseaux, pas de foo. :) Merci, cela aide un peu. Je pense que dans mon cas, il n'est pas viable de déplacer la logique dans les classes comme vous l'avez fait. Comme je l'ai expliqué ci-dessus, les objets sont des actions, et impliqué le code spécifique côté serveur.Bien qu'il ne soit pas exécuté du côté client, je ne sais pas si GWT serait heureux d'avoir ce genre de choses dans son code côté client. Hmmm pas vraiment sûr cependant. – pj4533

0

Tant qu'il n'y a pas trop de mises en œuvre de Foo, je déclarerais une méthode abstraite dans SomeInterface pour chaque sous-classe de Foo, et ont une classe abstraite renvoyer les appels vers une méthode par défaut qui est défini pour le type le plus général:

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); 
     } 
} 
Questions connexes