Лучший способ элементов в JComboBox

У меня есть JComboBox со многими элементами. Я добавил прослушиватель элементов в это поле со списком, в котором хранится выбранный элемент:

    comboBox.addItemListener(new ItemListener() {

        @Override
        public void itemStateChanged(ItemEvent e) {
            option = (String) e.getItem();
        }
    });

У меня есть кнопка, и при ее нажатии программа выполняет задачу на основе этого выбора.

Кнопка:

public void actionPerformed(ActionEvent e) {
  if (option.toLowerCase().compareTo("contrast stretching") == 0) { /* do sth */ } 
  else if (option.toLowerCase().compareTo("mean") == 0){ /* do sth else */ }
  // And many other else if statements

Функция actionPerformed слишком длинная. Как лучше написать код? Я не хочу делать одну функцию слишком длинной.


person coolscitist    schedule 11.08.2012    source источник


Ответы (3)


Вы можете создать интерфейс (например, Task), представляющий задачу, которую необходимо выполнить. Затем создайте реализацию этого интерфейса для каждого из значений в поле со списком (ContrastTask, MeanTask, ...). Наконец, в вашем actionPerformed напишите фабричный метод, возвращающий правильную реализацию - это будет в основном "переключатель", возвращающий правильный экземпляр Task. Тогда вы можете просто запустить эту задачу...

Task может выглядеть так:

public interface Task {
  [whatever result it should return] execute([whatever parameters it needs]);
}

Одна из реализаций может выглядеть так:

public class MeanTask implements Task {
  public int execute(int a, int b) {
    return (a + b) / 2;
  }
}

Ваша фабрика будет выглядеть так:

private Task create(String option) {
  if (option.toLowerCase().compareTo("mean") == 0) {
    return new MeanTask();
  }
  else if ....
}

Преимущество этого в том, что логика каждой задачи прекрасно инкапсулирована в свой собственный класс.

person vektor    schedule 11.08.2012
comment
Задачи, которые мне нужно написать, представляют собой очень короткие (3-4) строки кода. Должен ли я создавать новый класс для каждой задачи? Не слишком ли много классов? - person coolscitist; 11.08.2012
comment
@Robik, у тебя нет особого выбора, если ты хочешь сделать метод короче. Также это разделение поможет вам, если код будет развиваться и вдруг станет больше 3-4 строк :) - person vektor; 11.08.2012
comment
@zip: Да, я хочу внести изменения в графический интерфейс (изменение BufferedImage, отображаемого на панели). Я никогда не использовал SwingWorker. Я действительно не понял часть SwingWorker.invokeLater(). Что я должен написать в функции run()? Как лучше организовать код? - person coolscitist; 11.08.2012
comment
@Robik: Я сказал ерунду. Вам нужно использовать SwingWorker только в том случае, если вы выполняете длительную задачу (например, полное сканирование жесткого диска), которая занимает несколько минут. Если ваш код в вашем методе будет очень быстрым, вам не нужно учитывать какие-либо дополнительные вещи. Но в вашем сценарии это не так. Я неправильно понял ваш вопрос. Извини за это. - person zip; 11.08.2012
comment
@vektor Хорошо. Я просто хочу знать, как это делают хорошие программисты. Они тоже так делают? - person coolscitist; 11.08.2012
comment
@zip Это лучший способ? Как это делают профессиональные кодеры? - person coolscitist; 11.08.2012
comment
@Robik, да, ознакомьтесь с шаблонами методов Strategy и Factory: en.wikipedia.org/wiki/Strategy_pattern и en.wikipedia.org/wiki /Factory_method_pattern Я рекомендовал вам комбинацию этих двух методов. - person vektor; 11.08.2012
comment
Я бы использовал комбинацию между обоими ответами. Мне нравится векторный интерфейс. Но мне не нравится метод создания. Я публикую свой ответ и показываю его вам. Как бы это сделали профессиональные кодеры — неправильный вопрос. Я видел так много замороченного кода от профессиональных программистов. Так что не волнуйтесь. Вы делаете хорошо, думая о том, что вы хотите сделать, и просто делая свои собственные предположения, и вы хороши. - person zip; 11.08.2012
comment
Собственно теперь у меня другой вопрос. Есть ли способ вызвать функцию из строки. Например: вызовите mean+Task() (т. е. вызовите meanTask(), но с использованием строкового значения). Это автоматически вызовет функцию. Мне просто нужно было бы написать такие функции, как meanTask() и так далее для каждой задачи. - person coolscitist; 11.08.2012
comment
В этом случае использование API отражения действительно странно. Вы не должны усложнять свой подход. - person evg; 11.08.2012
comment
Ok. Я имею в виду, что create(String option){} делает то же самое. Использование строки для определения используемого класса. Нет ли другого способа сделать это автоматически, кроме Reflection API? - person coolscitist; 11.08.2012
comment
@Robik Просто используйте карту, чтобы получить задание. Это очень просто. Евгений подсказал, как это сделать. Использование Reflections в этом сценарии возможно, но я категорически не буду его использовать. - person zip; 11.08.2012
comment
Ok. Я постараюсь использовать оба решения. - person coolscitist; 11.08.2012
comment
@Robik, должен быть момент, когда вы объясняете Java, какая строка соответствует какой логике/задаче/методу/классу/... Либо это можно сделать неявно, через Reflection, либо вам нужно укажите это явно, перечислив все пары (строка - логика)... Извините, здесь нет волшебной палочки. - person vektor; 11.08.2012
comment
Я думал о том, что используются стандартные соглашения об именах шаблонов MVC, поэтому вещи сопоставляются автоматически (в некоторой степени). Я пытался повторить что-то подобное. Но опять же, у фреймворков MVC есть внутренний код, который выполняет сопоставление. Так что мне пришлось бы написать карту самостоятельно. Я понял теперь. - person coolscitist; 11.08.2012

Это может помочь вам:

public class Main {
  private Map<String, BaseStrategy> strategyMap = new HashMap<String, BaseStrategy>();

  {
    strategyMap.put("case1", new Strategy1());
    strategyMap.put("case2", new Strategy2());
    strategyMap.put("case3", new Strategy3());
  }

  //...

  public void actionPerformed(ActionEvent e) {
    strategyMap.get(option.toLowerCase()).processEvent();
  }

  abstract class BaseStrategy {

    public abstract void processEvent();

  }

  class Strategy1 extends BaseStrategy {

    @Override
    public void processEvent() {
      //case 1
    }
  }

  class Strategy2 extends BaseStrategy {

    @Override
    public void processEvent() {
      //case 2
    }
  }

  class Strategy3 extends BaseStrategy {

    @Override
    public void processEvent() {
      //case 3
    }
  }
}

Вы можете создать карту, где ключ — это строка, определяющая вашу команду, а значение — это стратегия, которая обрабатывает ваше событие. Теперь вы можете поместить свой код обработки даже в другие java-файлы и обработать событие всего одной строкой кода.


На самом деле, если у вас слишком много случаев - Map намного лучше, чем if-else-if-... Карта найдет правильную стратегию намного быстрее - O(ln n) вместо O(n) .

person evg    schedule 11.08.2012
comment
Классы, которые мне нужны (те, которые вы расширяете из BaseStrategy), будут очень короткими, всего 3-4 строки. Но количество классов, которые мне нужны, было бы очень большим. Кроме того, класс имеет только одну функцию. Это так делают профессионалы? Я имею в виду, это стандартный способ сделать это? - person coolscitist; 11.08.2012
comment
Да, вполне нормально создавать классы столько, сколько вы хотите. Обычно для этих сценариев вы создаете анонимный класс new Task() { /*my implementation*/}без определения явного класса. - person zip; 11.08.2012
comment
Стандартного пути нет - все зависит от требований. Если вы не можете сделать свою логику универсальной (я имею в виду сделать 1 функцию с параметрами), вам придется где-то написать этот код. В Win32API люди пишут длинный переключатель, который обрабатывает системные сообщения. Все зависит от стиля и требований. Лично я не вижу ничего плохого в том, чтобы собрать пакет со всеми этими стратегиями. Это позволяет вам довольно легко расширить свою логику. - person evg; 11.08.2012
comment
На самом деле, если у вас слишком много случаев - карта намного лучше, чем если-иначе-если-... Карта найдет правильную стратегию намного быстрее - O (ln n) вместо O (n). - person evg; 11.08.2012
comment
Ok. Так что я тоже буду использовать Map. Спасибо - person coolscitist; 11.08.2012

Как сообщили вектор и Юджин, я бы объединил оба решения примерно так:

public interface Task {
  [whatever result it should return] execute([whatever parameters it needs]);
}

//call this method in your constructor
private void initTasks() {
    this.tasks.put("option1", new Task() {
                public int execute(int a, int b) {
                   return (a + b) / 2;
                }
          });
    //init more tasks
    //this.task.put(..., ...);
}

protected Task getTask(String option) {
     return this.task(option);
}   
person zip    schedule 11.08.2012
comment
Проблема с этим очевидна: ваш initTasks() даже длиннее исходного метода! Помните, задание было сделать его короче ;) - person vektor; 11.08.2012