Dobre praktyki programistyczne

Sterowanie kodem porzez String

27.12.2010

Motywacja

Często zdarza się, że programy są sterowane za pomocą krótkich poleceń w postaci tekstowej. Mogą one pochodzić od użytkownika, zostać odczytane z pliku lub odebrane od innego programu. Dzięki temu komunikacja z takim programem staje się prostsza i czytelniejsza dla innych programów. Jest to szczególny i uproszczony przypadek wzorca projektowego Interpreter.

Zastosowania

  • Programy, którym podaje się parametry w linii poleceń.
  • Serwlety, do których przesłane są dane metodą GET lub POST.
  • Aplety na stronach www, gdzie w kodzie HTML umieszcza się parametry.

Ze względu na wydajność, najlepiej gdy interpretowanych będzie wiele grup parametrów podczas jednego uruchomienia kodu.

Przykład

Oto prosta aplikacja, która w zależności od podanych w linii poleceń parametrów wykonuje wybraną akcję.

  1. import java.util.*;
  2.  
  3. public class Main {
  4. private static final Map map;
  5.  
  6. static {
  7. Map l = new HashMap(3);
  8. l.put("help", new Help());
  9. l.put("add", new Add());
  10. l.put("upper", new UpperCase());
  11. map = Collections.unmodifiableMap(l);
  12. }
  13.  
  14. private static void help() {
  15. System.out.println("Try 'java -jar interpreter.jar help' for more information");
  16. }
  17.  
  18. public static void main(String[] args) {
  19. if (args.length > 0) {
  20. Action action = map.get(args[0]);
  21. if (action != null) {
  22. String[] params = new String[args.length - 1];
  23. System.arraycopy(args, 1, params, 0, params.length);
  24. action.execute(params);
  25. } else {
  26. help();
  27. }
  28. } else {
  29. help();
  30. }
  31. }
  32. }

Klasa Main uruchamia się na początku. Najpierw tworzona jest mapa polecenie=>akcja. W metodzie main pobiera się parametry, a pierwszy z nich identyfikuje podejmowaną akcję. Pozostałe służą jako ewentualne dodatkowe argumenty dla metody execute.

Diagram klas

Wszystkie akcje implementują interface Action, dzięki czemu możliwe jest wywołanie metody execute dla obiektu pobranego z mapy. Klasa Help wyświetla pomoc dotyczącą programu, Add – dodaje 2 liczby, a UpperCase wyświetla wszystkie argumenty za pomocą dużych liter.

Poniżej podano sposoby wywołania programu i wyniki jego działania.

Terminal   
  1. Help: java -jar interpreter.jar help
  2. Usage: java -jar interpreter.jar [options]
  3. add a b Add a and b then display sum
  4. upper Display all arguments as upper letters
  5.  
  6. Add: java -jar interpreter.jar add 13 62
  7. Sum = 75
  8.  
  9. Upper case: java -jar interpreter.jar upper Hello World!
  10. HELLO
  11. WORLD!
  12.  
  13. Any other: java -jar interpreter.jar something
  14. Try 'java -jar interpreter.jar help' for more information
  15.  
  16. No arguments: java -jar interpreter.jar
  17. Try 'java -jar interpreter.jar help' for more information

Modyfikacje

Jest wiele możliwości zmiany powyższego kodu. Dotyczy to głównie miejsca i sposobu generowania mapy. Przy dużej liczbie akcji, zamiast bezpośredniego dodawania obiektów do mapy, można utworzyć osobne tablice dla poleceń i akcji, a następnie dodawać je w pętli:

  1. public static void main(String[] args) {
  2. String[] commands = new String[] { "help", "add", "upper" };
  3. Action[] actions = new Action[] { new Help(), new Add(), new UpperCase() };
  4. Map map = new HashMap(commands.length);
  5. for (int i = 0; i < commands.length; i++) {
  6. map.put(commands[i], actions[i]);
  7. }
  8. }

Podsumowanie

Wzorzec ten zwiększa czytelność kodu, gdyż unikamy długich instrukcji if...else albo switch. Minusem jest konieczność utworzenia obiektu każdej akcji przed rozpoczęciem interpretowania, nawet jeśli nie będą wykonywane w trakcie działania programu.

Pobierz cały kod interpreter.jar (źródła+class).

Leave a Response