web-dev-qa-db-fra.com

Liste longue des instructions if dans Java

Désolé, je ne trouve pas de question pour y répondre, je suis presque certain que quelqu'un d'autre l'a déjà soulevée.

Mon problème est que j'écris des bibliothèques système pour exécuter des périphériques intégrés. J'ai des commandes qui peuvent être envoyées à ces appareils via des émissions radio. Cela ne peut se faire que par texte. à l'intérieur des bibliothèques système, j'ai un fil qui gère les commandes qui ressemble à ceci

if (value.equals("A")) { doCommandA() }
else if (value.equals("B")) { doCommandB() } 
else if etc. 

Le problème est qu'il y a beaucoup de commandes qui vont rapidement devenir quelque chose hors de contrôle. Horrible à regarder, pénible à déboguer et époustouflant à comprendre dans quelques mois.

98
Steve

en utilisant modèle de commande :

public interface Command {
     void exec();
}

public class CommandA() implements Command {

     void exec() {
          // ... 
     }
}

// etc etc

puis construisez un Map<String,Command> objet et remplissez-le avec des instances Command:

commandMap.put("A", new CommandA());
commandMap.put("B", new CommandB());

alors vous pouvez remplacer votre chaîne if / else if par:

commandMap.get(value).exec();

[~ # ~] modifier [~ # ~]

vous pouvez également ajouter des commandes spéciales telles que UnknownCommand ou NullCommand, mais vous avez besoin d'un CommandMap qui gère ces cas d'angle afin de minimiser les vérifications du client.

167
dfa

Ma suggestion serait une sorte de combinaison légère d'énumération et d'objet Command. Il s'agit d'un idiome recommandé par Joshua Bloch dans l'article 30 de Java efficace.

public enum Command{
  A{public void doCommand(){
      // Implementation for A
    }
  },
  B{public void doCommand(){
      // Implementation for B
    }
  },
  C{public void doCommand(){
      // Implementation for C
    }
  };
  public abstract void doCommand();
}

Bien sûr, vous pouvez transmettre des paramètres à doCommand ou avoir des types de retour.

Cette solution peut ne pas être vraiment appropriée si les implémentations de doCommand ne "correspondent" pas vraiment au type enum, qui est - comme d'habitude lorsque vous devez faire un compromis - un peu flou.

12
jens

Avoir une énumération de commandes:

public enum Commands { A, B, C; }
...

Command command = Commands.valueOf(value);

switch (command) {
    case A: doCommandA(); break;
    case B: doCommandB(); break;
    case C: doCommandC(); break;
}

Si vous avez plusieurs commandes, essayez d'utiliser le modèle Command, comme indiqué ailleurs (bien que vous puissiez conserver l'énumération et incorporer l'appel à la classe d'implémentation dans l'énumération, au lieu d'utiliser un HashMap). Veuillez voir la réponse d'Andreas ou jens à cette question pour un exemple.

7
JeeBee

La mise en œuvre d'une interface comme démontré simplement et clairement par dfa est propre et élégante (et prise en charge "officiellement"). C'est à cela que sert le concept d'interface.

En C #, nous pourrions utiliser des délégués pour les programmeurs qui aiment utiliser les pointeurs functon en c, mais la technique de DFA est la façon d'utiliser.

Vous pourriez aussi avoir un tableau

Command[] commands =
{
  new CommandA(), new CommandB(), new CommandC(), ...
}

Ensuite, vous pouvez exécuter une commande par index

commands[7].exec();

Plagiat à partir de DFA, mais ayant une classe de base abstraite au lieu d'une interface. Remarquez la cmdKey qui serait utilisée plus tard. Par expérience, je me rends compte que souvent une commande d'équipement a aussi des sous-commandes.

abstract public class Command()
{
  abstract public byte exec(String subCmd);
  public String cmdKey;
  public String subCmd;
}

Construisez ainsi vos commandes,

public class CommandA
extends Command
{
  public CommandA(String subCmd)
  {
    this.cmdKey = "A";
    this.subCmd = subCmd;
  }

  public byte exec()
  {
    sendWhatever(...);
    byte status = receiveWhatever(...);
    return status;
  }
}

Vous pouvez ensuite étendre HashMap générique ou HashTable en fournissant une fonction de succion de paire clé-valeur:

public class CommandHash<String, Command>
extends HashMap<String, Command>
(
  public CommandHash<String, Command>(Command[] commands)
  {
    this.commandSucker(Command[] commands);
  }
  public commandSucker(Command[] commands)
  {
    for(Command cmd : commands)
    {
      this.put(cmd.cmdKey, cmd);
    }
  }
}

Construisez ensuite votre magasin de commandes:

CommandHash commands =
  new CommandHash(
  {
    new CommandA("asdf"),
    new CommandA("qwerty"),
    new CommandB(null),
    new CommandC("hello dolly"),
    ...
  });

Vous pouvez maintenant envoyer des contrôles de manière objective

commands.get("A").exec();
commands.get(condition).exec();
7
Blessed Geek

Eh bien, je suggère de créer des objets de commande et de les mettre dans une table de hachage en utilisant la chaîne comme clé.

5
keuleJ

Même si je crois que l'approche du modèle de commande est davantage axée sur les meilleures pratiques et peut être maintenue à long terme, voici une option pour vous:

org.Apache.commons.beanutils.MethodUtils.invokeMethod (this, "doCommand" + value, null);

3
svachon

Il vaut probablement mieux utiliser une carte de commandes.

Mais si vous en avez un ensemble pour vous gérer, vous vous retrouvez avec des tas de cartes qui frappent. Ensuite, cela vaut la peine de le faire avec Enums.

Vous pouvez le faire avec une énumération sans utiliser de commutateurs (vous n'avez probablement pas besoin des getters dans l'exemple), si vous ajoutez une méthode à l'énumération pour résoudre la "valeur". Ensuite, vous pouvez simplement faire:

Mise à jour: ajout d'une carte statique pour éviter l'itération à chaque appel. Impudiquement pincé de cette réponse .

Commands.getCommand(value).exec();

public interface Command {
    void exec();
}

public enum Commands {
    A("foo", new Command(){public void exec(){
        System.out.println(A.getValue());
    }}),
    B("bar", new Command(){public void exec(){
        System.out.println(B.getValue());
    }}),
    C("barry", new Command(){public void exec(){
        System.out.println(C.getValue());
    }});

    private String value;
    private Command command;
    private static Map<String, Commands> commandsMap;

    static {
        commandsMap = new HashMap<String, Commands>();
        for (Commands c : Commands.values()) {
            commandsMap.put(c.getValue(), c);    
        }
    }

    Commands(String value, Command command) {
        this.value= value;
        this.command = command;
    }

    public String getValue() {
        return value;
    }

    public Command getCommand() {
        return command;
    }

    public static Command getCommand(String value) {
        if(!commandsMap.containsKey(value)) {
            throw new RuntimeException("value not found:" + value);
        }
        return commandsMap.get(value).getCommand();
    }
}
2
Rich Seller

La réponse fournie par @dfa est à mon avis la meilleure solution.

Je ne fais que fournir des extraits au cas où vous utilisez Java 8 et que vous souhaitez utiliser Lambdas!

Commande sans paramètres:

Map<String, Command> commands = new HashMap<String, Command>();
commands.put("A", () -> System.out.println("COMMAND A"));
commands.put("B", () -> System.out.println("COMMAND B"));
commands.put("C", () -> System.out.println("COMMAND C"));
commands.get(value).exec();

(vous pouvez utiliser un Runnable au lieu de Command, mais je ne le considère pas sémantiquement correct):

Commande avec un paramètre:

Si vous attendez un paramètre, vous pouvez utiliser Java.util.function.Consumer:

Map<String, Consumer<Object>> commands = new HashMap<String, Consumer<Object>>();
commands.put("A", myObj::doSomethingA);
commands.put("B", myObj::doSomethingB);
commands.put("C", myObj::doSomethingC);
commands.get(value).accept(param);

Dans l'exemple ci-dessus, doSomethingX est une méthode présente dans la classe de myObj qui prend tout objet (nommé param dans cet exemple) comme argument.

2
Marlon Bernardes

j'essaie généralement de le résoudre de cette façon:

public enum Command {

A {void exec() {
     doCommandA();
}},

B {void exec() {
    doCommandB();
}};

abstract void exec();
 }

cela présente de nombreux avantages:

1) il n'est pas possible d'ajouter une énumération sans implémenter exec. vous ne manquerez donc pas un A.

2) vous n'aurez même pas à l'ajouter à une carte de commande, donc pas de code passe-partout pour construire la carte. juste la méthode abstraite et ses implémentations. (qui est sans doute aussi passe-partout, mais il ne raccourcira pas ..)

3) vous économiserez tout cycle de processeur perdu en parcourant une longue liste de si ou en calculant des codes de hachage et en effectuant des recherches.

edit: si vous n'avez pas d'énumérations mais des chaînes comme source, utilisez simplement Command.valueOf(mystr).exec() pour appeler la méthode exec. notez que vous devez utiliser le modificateur public sur execif que vous souhaitez appeler depuis un autre package.

2
Andreas Petersson

si vous avez plusieurs instructions 'if' imbriquées, alors il s'agit d'un modèle d'utilisation d'un moteur de règles . Voir, par exemple JBOSS Drools .

1
Pierre

Utilisez simplement un HashMap, comme décrit ici:

0
ars

s'il était possible d'avoir un tableau de procédures (ce que vous appelez des commandes) qui seraient utiles.

mais vous pourriez écrire un programme pour écrire votre code. Tout est très systématique if (value = 'A') commandA (); sinon si (........................ e.t.c.

0
user147042

Je ne sais pas si vous avez un chevauchement entre le comportement de vos différentes commandes, mais vous voudrez peut-être également jeter un œil au modèle Chain Of Responsibility qui pourrait offrir plus de flexibilité en permettant à plusieurs commandes de gérer certaines valeurs d'entrée.

0
tinyd

Le modèle de commande est la voie à suivre. Voici un exemple utilisant Java 8:

1. Définissez l'interface:

public interface ExtensionHandler {
  boolean isMatched(String fileName);
  String handle(String fileName);
}

2. Implémentez l'interface avec chacune des extensions:

public class PdfHandler implements ExtensionHandler {
  @Override
  public boolean isMatched(String fileName) {
    return fileName.endsWith(".pdf");
  }

  @Override
  public String handle(String fileName) {
    return "application/pdf";
  }
}

et

public class TxtHandler implements ExtensionHandler {
  @Override public boolean isMatched(String fileName) {
    return fileName.endsWith(".txt");
  }

  @Override public String handle(String fileName) {
    return "txt/plain";
  }
}

etc .....

. Définissez le client:

public class MimeTypeGetter {
  private List<ExtensionHandler> extensionHandlers;
  private ExtensionHandler plainTextHandler;

  public MimeTypeGetter() {
    extensionHandlers = new ArrayList<>();

    extensionHandlers.add(new PdfHandler());
    extensionHandlers.add(new DocHandler());
    extensionHandlers.add(new XlsHandler());

    // and so on

    plainTextHandler = new PlainTextHandler();
    extensionHandlers.add(plainTextHandler);
  }

  public String getMimeType(String fileExtension) {
    return extensionHandlers.stream()
      .filter(handler -> handler.isMatched(fileExtension))
      .findFirst()
      .orElse(plainTextHandler)
      .handle(fileExtension);
  }
}

4. Et voici l'exemple de résultat:

  public static void main(String[] args) {
    MimeTypeGetter mimeTypeGetter = new MimeTypeGetter();

    System.out.println(mimeTypeGetter.getMimeType("test.pdf")); // application/pdf
    System.out.println(mimeTypeGetter.getMimeType("hello.txt")); // txt/plain
    System.out.println(mimeTypeGetter.getMimeType("my presentation.ppt")); // "application/vnd.ms-PowerPoint"
  }
0
nxhoaf