Eviter l’instance lors de la vérification d’un type de message

Dans la situation suivante, une classe client exécute un comportement différent en fonction du type de message reçu. Je me demande s’il existe un meilleur moyen de le faire car je n’aime pas les exemples ofof et if.

Une chose à laquelle je pensais était de retirer les méthodes de la classe client et de les intégrer dans les messages. Je mettrais une méthode comme process () dans l’interface IMessage, puis le comportement spécifique au message dans chacun des types de message concrets. Cela simplifierait le client car il appellerait simplement message.process () plutôt que de vérifier les types. Cependant, le seul problème avec ceci est que le comportement contenu dans les conditions est lié aux opérations sur les données contenues dans la classe Client. Ainsi, si j’implémentais une méthode de traitement dans les classes de message concrètes, je devrais la transmettre au client et je ne sais pas si cela a vraiment un sens.

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

Le moyen le plus simple d’éviter une instance de test consiste à envoyer de manière polymorphe; par exemple

 public class Client { void messageReceived(IMessage message) { message.doOperations(this); } } 

où chaque classe de message définit une doOperations(Client client) appropriée.

EDIT: deuxième solution plus adaptée aux besoins.

Une alternative qui remplace une séquence de tests ‘instanceof’ par une instruction switch est la suivante:

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

Chaque classe IMessage doit définir une int getMessageType() pour renvoyer le code approprié. Les enums fonctionnent tout aussi bien et sont plus élégants, IMO.

Une option ici est une chaîne de gestionnaire . Vous avez une chaîne de gestionnaires, chacun pouvant gérer un message (le cas échéant), puis le consumr , ce qui signifie qu’il ne sera pas transmis plus tard dans la chaîne. Vous définissez d’abord l’interface Handler :

 public interface Handler { void handle(IMessage msg); } 

Et puis la logique de la chaîne de traitement ressemble à ceci:

 List handlers = //... for (Handler h : handlers) { if (!e.isConsumed()) h.handle(e); } 

Ensuite, chaque gestionnaire peut décider de gérer / consumr un événement:

 public class MessageAHandler implements Handler { public void handle(IMessage msg) { if (msg instanceof MessageA) { //process message //consume event msg.consume(); } } } 

Bien sûr, cela ne supprime pas l’ instanceof s – mais cela signifie que vous n’avez pas un énorme bloc if-elseif-else-if-instanceof , qui peut être illisible

Quel type de système de messagerie utilisez-vous?

Beaucoup ont la possibilité d’append un filtre aux gestionnaires en fonction de l’en-tête du message ou du contenu. Si cela est pris en charge, vous créez simplement un gestionnaire avec un filtre basé sur le type de message, alors votre code est propre et sans qu’il soit nécessaire d’inclure un type ou un type de vérification (puisque le système de messagerie l’a déjà vérifié pour vous).

Je sais que vous pouvez le faire dans JMS ou dans le service des événements OSGi.

Étant donné que vous utilisez JMS, vous pouvez essentiellement procéder comme suit pour enregistrer vos auditeurs. Cela créera un écouteur pour chaque type de message unique.

  Ssortingng filterMsg1 = "JMSType='messageType1'"; Ssortingng filterMsg2 = "JMSType='messageType2'"; // Create a receiver using this filter Receiver receiverType1 = session.createReceiver(queue, filterMsg1); Receiver receiverType2 = session.createReceiver(queue, filterMsg2); receiverType1.setMessageHandler(messageType1Handler); receiverType2.setMessageHandler(messageType2Handler); 

Désormais, chaque gestionnaire recevra uniquement le type de message spécifique (pas d’instance of ou if-then), en supposant bien sûr que l’expéditeur définisse 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 y filtrer également.

 //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(Ssortingng[] args) { doSmth(new MessageA()); } public static void doSmth(Message message) { message.doOperations() ; } } 

Une solution Java 8 qui utilise la double répartition. Ne supprime pas complètement instanceof mais ne nécessite qu’une vérification par message au lieu d’une chaîne if-elseif.

 public interface Message extends Consumer> {}; public interface MessageA extends Message { @Override default void accept(Consumer consumer) { if(consumer instanceof MessageAReceiver){ ((MessageAReceiver)consumer).accept(this); } else { Message.super.accept(this); } } } public interface MessageAReceiver extends Consumer{ void accept(MessageA message); }