9
votes

Éviter l'instanceOf lors de la vérification d'un type de message

J'ai la situation suivante où une classe client exécute un comportement différent basé sur le type de message qu'il reçoit. Je me demande s'il y a une meilleure façon de le faire depuis que je n'aime pas l'instance de et les déclarations de si.

Une chose que je pensais faire était de tirer les méthodes de la classe client et de les mettre dans les messages . Je mettrais une méthode comme processus () dans l'interface IMessage, puis mettez le comportement spécifique au message dans chacun des types de messages concrets. Cela rendrait le client simple car il suffirait d'appeler le message.Processé () plutôt que de vérifier les types. Cependant, le seul problème est que le comportement contenu dans les conditionnes concerne les opérations sur les données contenues dans la classe cliente. Ainsi, si je faisais implémenter une méthode de processus dans les classes de messages concrètes, je devrais transmettre le client et je ne sais pas si cela est vraiment logique non plus. P>

public class Client {
    messageReceived(IMessage message) {
        if(message instanceof concreteMessageA) {
            concreteMessageA msg = (concreteMessageA)message;
            //do concreteMessageA operations
        }
    }
        if (message instanceof concreteMessageB) {
            concreteMessageb msg = (concreteMessageB)message;
            //do concreteMessageB operations
        }
}


1 commentaires

Sur la base du fait que vous utilisez jms, qu'est-ce que IMESSAGE? JMS a des types de messages prédéfinis pour que vous ne puissiez pas définir le vôtre, à moins que vous n'ayez un intermédiaire qui les convertira, ce qui est tout à fait de mal de tête à moins que vous n'essayez de résumer JMS.


6 Réponses :


7
votes

Le moyen simple d'éviter les tests d'instanceOF consiste à envoyer de manière polymorphicale; Par exemple,

public class Client {
    void messageReceived(IMessage message) {
        switch (message.getMessageType()) {
        case TYPE_A:
           // process type A
           break;
        case TYPE_B:
           ...
        }
    }
}


6 commentaires

La 2e approche pourrait toujours nécessiter une distribution si les différents blocs de processus nécessaires pour accéder aux propriétés uniquement disponibles sur les sous-types de messages


Je n'aime pas l'idée du client définissant la manière dont le message doit être traité. Que se passe-t-il si vous devez changer de Dooperations - comment obtenez-vous ce code mis à jour à tous les clients? Je suppose qu'un modèle de visiteur serait mieux dans ce cas. Afin que le visiteur du client soit accepté par le message et peut se faufiler pour faire ce qu'il veut.


@PGP: Euh ... Je pense que vous êtes mal compris le problème. La manière dont le problème est posé, la logique de traitement des messages >> est << spécifique à la classe client.


J'ai initialement pensé à utiliser le polymorphisme et à faire exactement ce que Stephen dit dans son premier échantillon de code, mais je pensais que cela risque de violer l'encapsulation en faisant passer le client aux messages. De plus, si j'avais 5 clients et chacun veut faire quelque chose de différent avec chaque message qui correspond à 5 opérations différentes dans chaque message un pour chacun des clients.


@volker: le passage de soi ne violera pas l'encapsulation. L'encapsulation n'est violée que si le client expose les états / méthodes qu'il ne devrait pas.


@Volker: Les "5 clients" utilisent-on peut être adressé par la sous-classement de la classe client, chacun surchargeant la méthode Messagereceed et éventuellement déléguant à Super.Messagereceeced pour traiter les types de messages courants.



5
votes

Une option ici est une chaîne manutention . Vous avez une chaîne de gestionnaires, chacune pouvant gérer un message (le cas échéant), puis consommer , ce qui signifie qu'il ne sera pas passé plus loin dans la chaîne. Tout d'abord, vous définissez l'interface manutention : xxx

puis la logique de chaîne de manutention ressemble à: xxx

alors Chaque gestionnaire peut décider de gérer / consommer un événement: xxx

bien sûr, cela ne se débarrasse pas de l'instance S - mais il fait signifie que vous n'avez pas d'énorme if-elseif-else-si-instanceof bloc, qui peut être illisible


2 commentaires

La lisibilité est une question d'opinion / goût. Création de nouvelles classes de manutention pour chaque type de message signifie que vous devez regarder quelque part ailleurs (en dehors de la classe client et des classes d'imessage) pour trouver la logique. Je n'appellerais pas qu'une "grosse lisibilité gagne".


Moi non plus! Tout dépend de si vous détestez BIG si-else-instanceof blocs. Cela semble comme si l'op le fait, alors je l'ai suggéré. La chaîne de manutention est utile cependant, car il est plus flexible que si-d'autre. Un gestionnaire n'a pas besoin de consommer un événement, il peut donc être traité par plus d'un gestionnaire



1
votes

Quel type de système utilisez-vous?

Beaucoup ont des options pour ajouter un filtre aux gestionnaires basés sur l'en-tête ou le contenu de message. Si tel est pris en charge, vous créez simplement un gestionnaire avec un filtre basé sur le type de message, puis votre code est agréable et propre sans le besoin d'instanceOf ou de type de vérification (puisque le système de messagerie vous a déjà vérifié pour vous). < P> Je sais que vous pouvez le faire dans JMS ou le service d'événements OSGI.

Comme vous utilisez JMS, vous pouvez utiliser essentiellement les éléments suivants pour enregistrer vos auditeurs. Cela créera un auditeur pour chaque type de message unique. xxx

Maintenant, chaque gestionnaire recevra le type de message spécifique uniquement (aucune instance of of of OU alors), en supposant bien sûr que l'expéditeur définit le type via des appels à Setjmstype () sur le message sortant.

Cette méthode est intégrée au message, mais vous pouvez bien sûr créer votre propre propriété d'en-tête et filtrer à cela aussi.


3 commentaires

J'utilise JMS et je vais devoir examiner votre suggestion.


@ Volker238 - C'est ce qu'on appelle un sélecteur et vous pouvez l'utiliser pour filtrer en fonction des informations d'en-tête de message.


@ Volker238 - J'ai ajouté un extrait de code en fonction du fait que vous utilisez JMS. En supposant que vous puissiez définir des propriétés d'en-tête, le sélecteur est absolument la voie à suivre puisqu'il permet à la mise en œuvre du JMS de faire tout le routage pour vous. Pas besoin d'un code de manipulation laids que vous roulez vous-même.



0
votes
//Message.java

abstract class Message{
     public abstract void doOperations();
 }

//MessageA.java

 class MessageA extends Message{
    public void doOperations(){ 
         //do concreteMessageA operations ;
    }
 }

   //MessageB.java

class MessageB extends Message {
     public void doOperations(){ 
         //do concreteMessageB operations 
     }
}

//MessageExample.java

class MessageExample{
   public static void main(String[] args) {
        doSmth(new MessageA());
    }

    public static void doSmth(Message message) {
       message.doOperations() ;     

     }
}   

0 commentaires

0
votes

une solution Java 8 qui utilise une double envoi. Ne se débarrasse pas de instance de code> complètement mais ne nécessite qu'un seul contrôle par message au lieu d'une chaîne if-elseif.

public interface Message extends Consumer<Consumer<Message>> {};

public interface MessageA extends Message {

   @Override
   default void accept(Consumer<Message> consumer) {
      if(consumer instanceof MessageAReceiver){
        ((MessageAReceiver)consumer).accept(this);
      } else {
        Message.super.accept(this);
      }
   }
}

public interface MessageAReceiver extends Consumer<Message>{
   void accept(MessageA message);
}


0 commentaires

0
votes

avec JMS 2.0 Vous pouvez utiliser:

consumer.receiveBody(String.class)


0 commentaires