Как инициализировать поля ввода без написания «if операторов»? - PullRequest
3 голосов
/ 27 мая 2011

У меня есть перечисление как

public enum Field {
     A, B, C, D, E ....;

     private Field(){
     }
}

У меня есть class Panel, который требует Field array для инициализации полей:

public class Panel {
     TextBox A; 
     TextBox B;
     TextBox C;
     TextBox D;
     TextBox E;
     ...


     public Panel(Field[] fields){
          this.fields = fields;
          init();
     }

     public void initA(){}
     public void initB(){}
     public void initC(){}
     public void initD(){}
     public void initE(){}
}

У меня вопрос, как я могу инициализировать данные поля, не записывая много if?

Я не могу найти никакого решения, и теперь я инициализирую так:

public void init(){
      for(int i = 0 ; i < fields.length; i++){
          if(fields[i] == Field.A){
              initA();
          } else if(fields[i] == Field.B){
              initB();
          } else if(fields[i] == Field.C){
              initC();
          } else if(fields[i] == Field.D){
              initD();
          } else if(fields[i] == Field.E){
              initE();
          }  ....
      }
}

Ответы [ 10 ]

5 голосов
/ 27 мая 2011

Звучит так, будто ваш дизайн, возможно, стоит рассмотреть. Несколько предложений:

  • Добавьте метод init в ваше перечисление. Так тогда вы можете перебирать массив ваших перечислений и позвонить в init метод, так что перечисление знает, как сделать свою собственную инициализацию
  • создать объект Command, который делает инициализация и создать Карта вашего перечисления в качестве ключа и Команда в качестве значения. цикл вокруг карты, управляющей Командой за каждое перечисление.
  • Используйте рефлексию - с точки зрения затрат, я бы не стал слишком беспокоиться об этом, если ваша система не работает с невероятно низкой задержкой

Для первого маркера вы можете изменить TextBox, чтобы он содержал тип поля, например

TextBox A = new TextBox(Field.A);
TextBox B = new TextBox(Field.B);

Так что, если TextBox знает, что это A, B, C, D, E, тогда вам просто нужно обойти ваше Field [], и когда он найдет свое преобразование TextBox, запустите код инициализации (который может быть сохранен для конкретного экземпляра enum). ). Конечно, вам нужно будет где-то зарегистрировать все ваши экземпляры TextBox в структуре данных, так как вы, похоже, очень против использования очень широко используемого API отражения.

По сути, должна быть связь между полем и TextBox. Ява не может читать ваши мысли и знать это без вашего ведома. Ну, по крайней мере, пока Google не представит свой API телепатии (и это, вероятно, будет только для Go ...). Это может быть сделано на основе именования (отражения), жестко закодированной логики (если или переключается) или на основе состояния. Для последнего это означает связывание поля с TextBox, как я продемонстрировал на примере конструктора выше.

1 голос
/ 27 мая 2011

Вот фрагмент, показывающий добавление метода init к перечислению.В методе init каждого поля вы можете вызывать один из ваших различных методов initX ().Создание абстрактного метода init заставляет компилятор напоминать вам определить ваш метод init для значения enum.

1 голос
/ 27 мая 2011

Как упоминалось в @planetjones, вы можете добавить метод init() в свой класс enum.Метод init должен возвращать ссылку на инициализированный TextBox его (enum) типа.Если вам нужно передать данные инициализатору, вы можете передать this, чтобы он мог получить любую необходимую ему информацию.

Чтобы обойти проблему поиска переменной, которую нужно назначить, вы можете объявить массивиз TextBox es

public void init(){
  for(int i = 0 ; i < fields.length; i++){
      F[i] = fields[i].init(this);
  }
}

или назначьте их после инициализации временного массива.

public void init(){
  TextBox F[5];
  for(int i = 0 ; i < fields.length; i++){
      F[i] = fields[i].init(this);
  }
  A = F[0];
  B = F[1];
  C = F[2];
  D = F[3];
  E = F[4];
}

Конечно, вы должны объявлять константы вместо использования магических чисел.

1 голос
/ 27 мая 2011

С точки зрения дизайна я бы выбрал комбинацию Заводской шаблон , Синглтон-шаблон (на основе enum) и Командный шаблон .Я вижу набор команд , где каждая команда является определенной для данного значения. factory ( Singleton ) - это общий шаблон для создания таких специализированных экземпляров. Несмотря на то, что он просто перемещает цепочку if / switch на фабрику (но фабрикам разрешено использовать условные проверки для создания экземпляров ..).

// the init command
public interface PanelInitializer {
  public init(Panel p);
}

// the factory
public enum PanelInitializerFactory {
  INSTANCE;

  public PanelInitializer create(Field field) {
    switch (field) {
      case A: return new TypeAInitializer();
      case B: return new TypeBInitializer();
      case C: return new TypeCInitializer();
      //..
    }
  }
}

Я не думаю, что мы можем избавиться от всех условных проверок без использования соглашений об именах и отражении / реализации или без введения ограничения, что все инициализаторы совместно используют один и тот же код.

0 голосов
/ 27 мая 2011

Начните с этого примера:

import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class FooPanelMain {

    public static void main(String[] args) {

        FooPanel panel = new FooPanel();
        JFrame frame = new JFrame();
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setVisible(true);
    }

}    

class FooPanel extends JPanel {

    // fields are dynamically created, so we put them into a map
    private Map<PanelField, JLabel> fields = new HashMap<PanelField, JLabel>();

    // enum to configure the fields
    private enum PanelField {
        FIRST("first text"), 
        SECOND("second text"), 
        LAST("last text");

        private String text;

        private PanelField(String text) {
            this.text = text;
        }

        public String getLabelName() {
            return text;
        }

    }
        // constructor uses the enum configuration to create the fields
    public FooPanel() {
        for (PanelField fooPanelField : PanelField.values()) {
            createLabel(fooPanelField);
        }
    }

    private void createLabel(PanelField field) {

        JLabel label = new JLabel(field.getLabelName());
        this.add(label);
        fields.put(field, label);
    }
}    

Этот пример можно легко превратить в абстрактное решение, определив интерфейс для PanelField, который реализуется перечислениями.FooPanel может использоваться как базовый класс для панелей.

0 голосов
/ 27 мая 2011

Ваш подход совсем не плох, если методы init, связанные с каждым TextBox, сильно отличаются, а список полей мал. Кроме того, если вы обычно создаете экземпляр только одного из этих экземпляров Panel, другие подходы могут принести больше вреда, чем помощи.

Сказав это, рассмотрите возможность использования java.util.EnumMap . После этого у вас есть три варианта:

  1. зарегистрировать TextBoxes и в другом массиве,
  2. вызывает initA, ... используя отражение или
  3. вызовите их, используя некоторую конструкцию функтора.

Лучший выбор зависит от варианта использования.

0 голосов
/ 27 мая 2011

Почему у вас текущая реализация плохая?Только потому что это выглядит "некрасиво"?Вы можете использовать switch вместо связки if:

  public void init(){
    for(int i = 0 ; i < fields.length; i++){
        switch(fields(i)){
          case A:
            initA();
            break
          case B:
          ...
        }
    }
  }

Может быть, логика в initA, initB ... очень похожа?Если у вас есть 20 различных перечислений и 20 различных инициализаций для запуска, не так много места для улучшений ...

0 голосов
/ 27 мая 2011

лучше всего переместить init в перечисление, как:

public enum Example{ 
  value1(1), 
  value2(2), 
  value3(66);

  private final int internalValue;

  Example(int value){ 
       this.internalValue = value; 
  }

  public int getInternalValue(){ 
       return this.internalValue; 
  }
}

Хотя это действительно простой пример, вы можете добавить любой код в конструктор позже и принимать более сложные решения на основе самого объекта.

0 голосов
/ 27 мая 2011

Вы можете сделать это, например, переместив логику инициализации в enum.Есть метод инициализации, который принимает TextBox в качестве параметра и инициализирует его.

Кстати, вам бы лучше иметь переменные TextBox в массиве.

EDIT Другой вариантЯ часто использую enum как хранилище архетипов.Там у меня есть метод, возвращающий объект, который соответствует определенному типу перечисления.

Если вы не хотите иметь инициализацию в enum, вы можете переместить его в объекты, которые вы собираетесь вернуть.Там для каждого конкретного объекта у вас будет отдельная инициализация.

Боюсь, что вы пытаетесь преследовать дракона с этим. Посмотрите на это так.Поскольку ваша проблема «условная», то есть вы должны выполнить различную инициализацию в зависимости от типа перечисления поля, поэтому в какой-то момент вам придется использовать оператор ifs или switch.

Поверьте, что нетволшебным путем, как программа должна знать, что вы хотите сделать?Даже используя отражение, вы будете использовать ifs и т. Д. Для его соответствующей инициализации.

0 голосов
/ 27 мая 2011

Вы можете использовать рефлексию java для циклического прохождения вашего перечисления, но вам действительно нужно найти способ объединить все ваши методы initN ().

...