Frage

Leider kann ich nicht eine Frage zu beantworten diese finden, ich bin fast sicher, jemand anderes es vor erhoben hat.

Mein Problem ist, dass ich einige System-Bibliotheken ich schreibe Embedded-Geräten laufen. Ich habe Befehle, die auf diese Geräte über Radiosendungen versendet werden können. Dies kann nur durch Text erfolgen. innerhalb der Systembibliotheken habe ich einen Faden, der die Befehle, die wie folgt aussieht

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

Das Problem ist, dass es eine Menge von Befehlen, um es ist schnell Spirale etwas außer Kontrolle geraten. Horrible zu achten, schmerzhaft zu debuggen und umwerfend in ein paar Monaten zu verstehen.

War es hilfreich?

Lösung

Befehlsmuster mit:

public interface Command {
     void exec();
}

public class CommandA() implements Command {

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

// etc etc

dann bauen ein Map<String,Command> Objekt und füllen Sie es mit Command Instanzen:

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

, dann können Sie ersetzen , wenn / else if Kette mit:

commandMap.get(value).exec();

Bearbeiten

Sie können auch spezielle Befehle wie UnknownCommand oder NullCommand hinzufügen, aber Sie brauchen einen CommandMap dass Griffe diese Ecke Fällen um Kunden Kontrollen zu minimieren.

Andere Tipps

Mein Vorschlag wäre eine Art leichter Kombination von ENUM und Command-Objekt sein. Dies ist ein Idiom von Joshua Bloch in Punkt 30 von Effective Java empfohlen.

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

Natürlich kann man Parameter DoCommand passieren könnte oder Rückgabetypen haben.

Diese Lösung ist vielleicht nicht wirklich geeignet sein, wenn die Implementierungen von DoCommand nicht wirklich „fit“ zu dem Aufzählungstyp, der - wie immer, wenn man einen Kompromiss machen muß - ein wenig unscharf.

Haben Sie eine ENUM von Befehlen:

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

Wenn Sie mehr als ein paar Befehle haben, Einblick in das Command-Muster verwenden, wie an anderer Stelle beantwortet (obwohl Sie die Enum behalten kann und betten den Aufruf der implementierenden Klasse innerhalb des Enum, anstatt eine HashMap zu verwenden). Bitte beachten Sie Andreas oder jens' Antwort auf diese Frage für ein Beispiel.

Die Implementierung einer Schnittstelle, wie einfach und klar von DFA gezeigt ist sauber und elegant (und „offiziell“ unterstützt Art und Weise). Das, was das Schnittstellenkonzept soll für.

In C #, wir Delegierten für Programmierer, die wie zu Verwendung functon Zeiger in c verwenden könnten, aber DFA-Technik ist die Art und Weise zu nutzen.

Sie können ein Array haben zu

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

Dann könnten Sie einen Befehl von Index ausführen

commands[7].exec();

Plagiat von DFA, aber statt einer Schnittstelle eine abstrakte Basisklasse mit. Beachten Sie die cmdKey, die später verwendet werden würde. Durch Erfahrung weiß ich, dass häufig ein Gerät commmand Subbefehle auch hat.

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

Erstellen Sie Ihre Befehle so,

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

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

Sie könnten dann generische HashMap oder HashTable erweitern durch einen Schlüssel-Wert-Paar Saugfunktion Bereitstellung:

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

Dann wird Ihren Befehl speichern konstruieren:

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

Nun könnte man Kontrollen senden objektiv

commands.get("A").exec();
commands.get(condition).exec();

Nun, ich schlage vor, Befehlsobjekte zu erstellen und sie in eine hashmap den String als Schlüssel verwenden setzen.

Auch wenn ich den Befehl Muster Ansatz glauben ist mehr in Richtung beste pratices und wartbar auf lange Sicht, hier ist ein Einzeiler Option für Sie:

org.apache.commons.beanutils.MethodUtils.invokeMethod (die "DoCommand" + Wert, null);

ich in der Regel versuchen, es auf diese Weise zu lösen:

public enum Command {

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

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

abstract void exec();
 }

Dies hat viele Vorteile:

1) ist es nicht möglich, eine Enum hinzuzufügen, ohne exec zu implementieren. so dass Sie nicht ein A verpassen.

2) Sie werden nicht einmal in jedem Befehl Karte hinzuzufügen, also kein Standardcode für die Karte zu bauen. nur die abstrakte Methode und ihre Implementierungen. (Das ist wohl auch vorformulierten, aber es wird nicht kürzer bekommen ..)

3) werden Sie, indem Sie durch eine lange Liste alle verschwendet CPU-Zyklen sparen von, wenn der oder Hashcodes Berechnung und Lookups zu tun.

edit: wenn Sie nicht Aufzählungen aber Strings als Quelle haben, nur Command.valueOf(mystr).exec() verwenden, um die exec-Methode aufzurufen. Beachten Sie, dass Sie den öffentlichen Modifikator verwenden müssen execif Sie es von einem anderen Paket anrufen möchten.

Sie sind wahrscheinlich am besten aus einer Karte von Befehlen.

Aber sind Sie eine Reihe von ihnen haben zu handhaben Sie mit vielen Karten am Ende klopfen zu. Dann lohnt es sich, zu tun, es mit Aufzählungen suchen.

Sie können es mit einem Enum zu tun, ohne Schalter zu verwenden (Sie wahrscheinlich nicht brauchen die Getter im Beispiel), wenn Sie eine Methode zum Enum zu lösen für „Wert“ hinzufügen. Dann können Sie einfach tun:

Update: hinzugefügt statische Karte zu vermeiden Iteration bei jedem Aufruf. Schamlos gekniffen von diese Antwort .

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

Die Antwort von @dfa vorgesehen ist die beste Lösung, meiner Meinung nach.

Ich bin die Bereitstellung nur ein paar Schnipsel , falls Sie verwenden Java 8 und wollen Lambdas nutzen zu können!

Befehl ohne Parameter auf:

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

(Sie ein Runnable statt Befehl verwenden könnten, aber ich es nicht semantisch richtig halten):

Befehl mit einem Parameter:

Falls Sie erwarten, dass ein Parameter Sie java.util.function.Consumer verwenden:

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

In dem obigen Beispiel doSomethingX ist ein Verfahren, in myObj Klasse, die ein beliebiges Objekt (benannt param in diesem Beispiel) als Argument.

Wenn Sie mehr schuppen haben ‚wenn‘ Aussagen, dann ist dies ein Muster für die Verwendung einer Regel-Engine . Siehe zum Beispiel JBOSS geifert .

Verwenden Sie einfach ein HashMap, wie hier beschrieben:

, wenn es möglich war, eine Reihe von Verfahren zu haben (was Sie Befehle genannt), die nützlich sein würde ..

, aber man könnte ein Programm schreiben, um Ihren Code zu schreiben. Es ist alles sehr systematisch if (value = 'A') Commanda (); sonst wenn(........................ e.t.c.

Ich bin mir nicht sicher, ob Sie eine Überschneidung zwischen dem Verhalten Ihrer verschiedenen Befehle, aber Sie möchten vielleicht auch einen Blick auf die Verantwortungskette Muster, das, indem mehr Befehle mehr Flexibilität bieten, könnte einige Eingabewerte zu behandeln.

Befehlsmuster ist der Weg zu gehen. Hier ist ein Beispiel unter Verwendung von Java 8:

1. Definieren Sie die Schnittstelle:

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

2. Implementieren Sie die Schnittstelle mit jedem der Erweiterung:

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

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

und

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

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

und so weiter .....

3. Definieren Sie den Auftraggeber:

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. Und das ist das Probenergebnis:

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

Wenn es eine Menge Dinge tut, dann wird es eine Menge Code sein, man kann wirklich von dem wegzukommen. Nur macht es leicht zu folgen, die Variablen sehr aussagekräftige Namen geben, können Kommentare helfen zu ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top