2009-02-26 4 views
0

Je construis une IA pour un jeu RTS. (Pour le Spring RTS engine, si quelqu'un est intéressé.) La façon dont je l'ai mis en place consiste principalement en une collection de composants qui communiquent au moyen d'événements déclenchés. Chaque composant a une méthode handleEvent (Event) qui reçoit les événements déclenchés par les autres composants.Surcharge d'une méthode d'une superclasse

L'événement est une interface. Une hiérarchie existe, chaque sous-classe fournissant des informations plus détaillées sur l'événement qu'ils représentent, auquel il est possible d'accéder en utilisant les méthodes getter spécifiques à cette classe. A titre d'exemple, la classe UnitGainedEvent implémente l'interface Event. Cette classe a une sous-classe UnitFinishedEvent (qui indique que la construction d'une unité ou d'un bâtiment est terminée, pour quiconque est curieux.)

Tous les composants ne seront pas intéressés par tous les événements, donc je voudrais laisser les composants choisir simplement les événements ils sont intéressés et ne reçoivent que ceux-là. En outre, l'ensemble des événements possibles est extensible, ce n'est donc pas une option valide pour laisser les composants ont des méthodes désignées pour chaque type d'événement. Au départ, je pensais que le modèle de visiteur pourrait m'aider avec ce problème, mais il échoue car il nécessite également un ensemble fixe de types d'événements. (Je ne suis pas sûr à 100% que je comprends correctement le modèle Corrigez-moi si je me trompe.)

Jusqu'à présent, la seule solution que j'ai trouvée est d'implémenter la méthode handleEvent (Event) de chaque composant quelque chose comme :

public int handleEvent(Event event) 
{ 
    if (event instanceof UnitGainedEvent) 
    { 
     UnitGainedEvent unitGainedEvent = (UnitGainedEvent) event; 
     // things to do if I lost a unit 
    } 

    else if (event instanceof UnitLostEvent) 
    { 
     UnitLostEvent unitLostEvent = (UnitLostEvent) event; 
     // things to do if I lost a unit 
    } 

    // etc. 
} 

Cependant, je n'aime pas vraiment avoir à distribuer les événements aux classes d'événements spécifiques. Maintenant, rappelant que la surcharge de méthode peut être utilisée pour invoquer différentes méthodes selon le type d'exécution d'un paramètre, j'ai rapidement trouvé une solution brillante, élégante comme simple: je pourrais créer une classe de base avec une implémentation vide de handleEvent (Event), et ont simplement des sous-classes qui reçoivent les événements qui les intéressent en créant une méthode handleEvent (UnitGainedEvent unitGainedEvent), par exemple. Voulant assurer qu'il travaillerait je mis en place un test rapide:

public class Main 
{ 
    public static void main(String[] args) 
    {  
     handleEvent(new UnitGainedEvent(null)); 
    } 

    public static void handleEvent(Event event) 
    { 
     System.out.println("Handling generic Event"); 
    } 

    public static void handleEvent(UnitGainedEvent event) 
    { 
     System.out.println("Handling UnitGainedEvent"); 
    } 
} 

Et à ma grande satisfaction, ce code imprime en fait « Manipulation UnitGainedEvent ». Donc, je me suis mis à la mise en œuvre.

Ma classe de base des composants ressemble à ceci: (Eh bien, pas vraiment Ceci est la classe composant dépouillé de tout pas pertinent au problème que je voudrais démontrer..)

public class Component 
{ 
    public void handleEvent(Event event) 
    { 
     System.out.println("Handling Event"); 
    } 
} 

Et c'est un exemple d'une sous-classe:

public class SomeComponent extends Component 
{ 
    public void handleEvent(UnitGainedEvent unitGainedEvent) 
    { 
     System.out.println("Handling UnitGainedEvent"); 
    } 
} 

afin de tester la configuration, j'utilise la classe principale suivante:

public class Main 
{ 
    public static void main(String[] args) 
    { 
     Component component = new SomeComponent(); 
     component.handleEvent(new UnitGainedEvent(null)); 
    } 
} 

Donc, je cours le code, et à ma grande surprise, le résultat est un «Handling Event» soigneusement imprimé. Chose intéressante, si je change le type de la variable de composant à SomeComponent, fait imprimer 'Handling UnitGainedEvent.' Pour une raison ou une autre, le système invoque aveuglément la méthode handleEvent (Event) de la classe Component, au lieu de surcharger handleEvent de SomeComponent (UnitGainedEvent). (Je serais intéressé d'entendre le raisonnement de Sun derrière cela, pensais que ce n'était pas vraiment pertinent à ma question - pas comme ils vont le réparer juste parce qu'une poignée de personnes trouveraient cela très utile.

Scouring the net me dit que d'autres personnes ont rencontré le même problème. Très peu de gens, à partir de la quantité infime d'informations que je trouve, mais les gens néanmoins, bien que je trouve plus d'informations sur la surcharge de la méthode générale et le dépassement que j'ai jamais voulu savoir. Cependant, à la fin, je n'arrive pas à trouver une solution. Maintenant, ma question (assez évidente) est, y a-t-il un moyen de contourner ce problème? A défaut, quelqu'un peut-il penser ou m'aider à trouver une autre solution tout aussi pratique? Editer: J'ai peine à croire que j'ai des réponses après seulement dix minutes. Je suis agréablement surpris. :) Cependant, la plupart des réponses jusqu'à présent suggèrent sous une forme ou une autre que je fais une méthode séparée pour chaque type d'événement possible. Techniquement, c'est possible, mais cela exigerait que je revienne dans le code et que j'ajoute une nouvelle méthode chaque fois que quelqu'un propose un nouveau type d'événement. Ce que j'ai appris est une mauvaise pratique de codage. (De plus, j'ai déjà plus de 20 types d'événements, et je n'ai même pas encore terminé.) Au lieu de cela, je préfère utiliser la solution impliquant le moulage, comme décrit ci-dessus. Au moins de cette façon, je peux m'assurer que les types d'événements inconnus sont simplement ignorés, me laissant libre de ne gérer que les événements où je veux les utiliser. J'espère vraiment une solution qui combine le meilleur des deux cependant. Pas de casting, et pas de retour dans mon code pour chaque type d'événement.

Un grand merci à l'avance, Loid Thanead

+0

Pour plus d'informations sur les raisons de cette situation, voir ici. http://stackoverflow.com/questions/321864/java-dynamic-binding-and-method-overriding/322234#322234 – Robin

Répondre

0

Lorsque vous envoyez un événement à un écouteur, examinez ses méthodes pour trouver une meilleure correspondance (ce que vous espériez que le compilateur ferait pour vous).

Vous voudriez évidemment mettre en cache les cibles d'invocation sinon les performances pourraient être douloureuses.

0

Alors je lance le code, et à ma grande surprise, le résultat est une manière ordonnée imprimé 'gestion des événements.' De manière intéressante, si je change le type de la variable de composant en SomeComponent, il imprime 'Handling UnitGainedEvent.' Pour une raison ou une autre, le système invoque aveuglément la méthode Component ' handleEvent (Event), à la place si la surcharger handleEvent SomeComponent (UnitGainedEvent).

ne fonctionne pas Surcharge vraiment de cette façon. Vous devez appeler la méthode surchargée sur la sous-classe, pas la superclasse. Puisque vous référencez le type de composant, Java ne sait pas que vous avez surchargé la méthode dans une sous-classe, il sait seulement si vous l'avez ou non.

Avez-vous vraiment besoin de la classe générique handleEvent (Event)? Pouvez-vous écrire à la place des méthodes handleEvent() pour les sous-classes d'événements spécifiques sans disposer d'un gestionnaire pour la superclasse Event?

0

Dans l'exemple ci-dessus, vous surchargez la méthode de la classe de base, mais vous ne la surchargez pas. Cela fonctionnerait comme prévu si vous définissiez une méthode abstraite (ou vide) gérant le UnitGainedEvent dans votre classe Component.

Vous pouvez envisager d'attaquer le problème sous un angle différent. Une approche que vous pourriez prendre est de faire quelque chose de similaire à ce qui est fait dans Swing avec toutes sortes d'auditeurs. Vous pouvez grouper les événements de manière logique et fournir un nombre d'écouteurs pour ces événements. Dans chaque groupe, vous pouvez ensuite fournir un adaptateur afin que les clients puissent les remplacer en ne traitant que les événements spécifiques qui les intéressent.

0

Il semble que vous soyez à la recherche d'arguments covariant, qui ne sont pas supportés par Java (les types de retour covariant sont cependant).

Pourquoi ne pas utiliser le observer pattern? Si un composant est toujours intéressé par des événements d'un type particulier, vous pouvez créer un registre statique et vous assurer que tous les composants concernés sont notifiés chaque fois qu'un événement de ce type est déclenché.

0

Le modèle de visiteur est la solution correcte. Fondamentalement, ce dont vous avez besoin est un envoi multiple. Vous voulez sélectionner une méthode basée sur le type de composant et le type d'événement qui n'est pas possible en Java. Jetez un oeil à cet article wikipedia pour plus d'informations: [http://en.wikipedia.org/wiki/Multiple_dispatch#Java][1].

[1]: http://Multiple Dispatch

0

Pour une raison ou une autre, le système invoque aveuglément la classe de composants handleEvent méthode (événement), au lieu si le de SomeComponent surcharge handleEvent (UnitGainedEvent). (Je intéressé à entendre ce raisonnement derrière Sun , pensé qu'il est pas vraiment pertinent à ma question - pas comme ils vont le réparer simplement parce qu'une poignée de personnes trouverait un très caractéristique utile.)

Le compilateur est "stupide". Changer votre code comme celui-ci et vous verrez pourquoi il doit être:

 
public class Main 
{ 
    public static void main(String[] args) 
    { 
     Component component = new SomeComponent(); 
     Event  event  = foo(); 

     component.handleEvent(event); 
    } 
} 

foo() retourne une sorte d'événement, aucune idée. Puisque le compilateur ne peut pas savoir quelle sous-classe est retournée et si le composant supporte cette méthode, tout ce qu'il peut faire est de travailler avec ce qu'il sait - que la classe Component a un handleEvent qui prend un événement.

Pour certains de ceux-ci, vous devriez regarder les types java.util.Event et java.util.EventObject. Utilisez la réflexion pour obtenir l'effet désiré.

Questions connexes