Должен ли я использовать слушателя или наблюдателя? - PullRequest
16 голосов
/ 13 апреля 2009

У меня есть выпадающий список в моем графическом интерфейсе, который показывает содержимое ArrayList в другом классе. Новые объекты могут быть добавлены в ArrayList в другом месте графического интерфейса, поэтому мне нужно знать, когда он обновляется, чтобы я мог обновить выпадающее меню. Из того, что я могу собрать, мои два варианта заключаются в том, чтобы расширить класс ArrayList, чтобы позволить мне добавить к нему свой собственный changeListener, или сделать класс, который содержит рассматриваемый ArrayList, расширенным для наблюдения.

Какое было бы более подходящее решение?

Ответы [ 6 ]

11 голосов
/ 13 апреля 2009

Два решения по сути являются реализациями одного и того же корневого шаблона проектирования (шаблон «Наблюдатель», как определено Группой четырех). В первом случае вы делаете сам ArrayList «наблюдаемым», во втором - сделать объект домена, использующий список массивов, "наблюдаемым".

Моя тенденция заключалась бы в том, чтобы сделать последнее: сделать объект домена видимым. В первую очередь это связано с тем, что в конечном итоге у вас могут появиться другие вещи, которые могут измениться об объекте домена (для которого следует обновить графический интерфейс.) Если он уже доступен для наблюдения, вы уже настроены.

Обратите внимание, что вам не обязательно расширять java.util.Observable - вы можете реализовать шаблон проектирования без этого.

9 голосов
/ 13 апреля 2009

Реализация Observable в Java используется редко и плохо взаимодействует с Swing. Вместо этого используйте EventListener.

В частности, есть ли причина не расширять AbstractListModel или даже использовать DefaultListModel непосредственно при управлении содержимым списка "в другом месте в GUI"? Тогда ваше поле со списком может использовать ComboBoxModel, который делегирует тот же экземпляр ListModel, добавляя свою собственную реализацию для отслеживания состояния выбора.

Я имею в виду что-то вроде этого (но я не проверял это):

final class MyComboBoxModel 
  extends AbstractListModel 
  implements ComboBoxModel 
{

  private final ListModel data;

  private volatile Object selection;

  MyComboBoxModel(ListModel data) { 
    /* 
     * Construct this object with a reference to your list, 
     * which contents are managed somewhere else in the UI.
     */
    this.data = data;
    data.addListDataListener(new ListDataListener() {
      public void contentsChanged(ListDataEvent evt) { 
        fireContentsChanged(this, evt.getIndex0(), evt.getIndex1()); 
      }
      public void intervalAdded(ListDataEvent evt) { 
        fireContentsChanged(this, evt.getIndex0(), evt.getIndex1()); 
      }
      public void intervalRemoved(ListDataEvent evt) { 
        fireContentsChanged(this, evt.getIndex0(), evt.getIndex1()); 
      }
    });
  }

  public void setSelectedItem(Object selection) { 
    this.selection = selection;
    fireContentsChanged(this, 0, data.getSize() - 1);
  }

  public Object getSelectedItem() { return selection; }

  public int getSize() { return data.getSize(); }

  public Object getElementAt(int idx) { return data.getElementAt(idx); }

}
2 голосов
/ 13 апреля 2009

Почему бы не использовать привязки?

http://wiki.eclipse.org/index.php/JFace_Data_Binding

Свяжите ваш GUI-виджет с вашим списком. Изменения будут распространяться между двумя объектами прозрачно. Обязательно оберните вашу модель соответствующим объектом наблюдения, таким как WritableList (если вы используете ArrayList напрямую).

1 голос
/ 13 апреля 2009

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

0 голосов
/ 21 апреля 2009

Если вы можете добавить новую банку в приложение, проверьте застекленные списки

0 голосов
/ 13 апреля 2009

Вы можете переключиться на использование шаблона проектирования GUI . Или построить ограниченную реализацию.

Создание интерфейса формы с графическим интерфейсом, в котором есть метод DrawXArrayList (где X - какое-то значимое имя. Он имеет параметры типа ArrayList

Создайте новый класс с именем GUIView. У него есть как минимум два метода: UpdateXArrayList и RegisterForm

Когда вы инициализируете свое приложение, форма GUI зарегистрируется в классе, реализующем GUIView. Сделайте класс, реализующий GUIView видимым для формы.

Когда что-либо в вашей форме GUI обновляет массив, он вызывает UpdateXArrayList как последнее, что он делает. Затем метод UpdateXArrayList в классе, реализующем GUIView, будет вызывать DrawXArrayList, передавая обновленный массив. DrawXArrayList в классе формы, реализующем GUIFormInterface, затем предпримет шаги, необходимые для обновления элемента управления, отображающего ArrayList.

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

...