Является ли ActionListener в контроллере для Java GUI App хорошей идеей?

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

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

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

PS Этот вопрос не дублирует шаблон MVC со многими ActionListeners

4 ответа

Решение

MVC не является "строгим" паттерном. Существуют разные интерпретации исходного шаблона и различные производные, такие как MVP или MVVM, которые часто используются (даже когда люди говорят, что используют MVC).

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

Наиболее частый вопрос, который возникает для шаблона MVC: "Что такое контроллер?"

Ответ:

"Бухгалтер, который получил повышение"

Из моего личного опыта редко бывает причина иметь явный класс "Контроллер". Принудительное накапливание и суммирование слушателей в одном классе "Контроллер" имеет несколько серьезных недостатков. Чтобы установить соединение между компонентами графического интерфейса и моделью, у вас есть два варианта: один вариант - разрешить доступ к компонентам представления для подключения слушателей:

gui.getSomeButton().addActionListener(myActionListener);

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

gui.addActionListenerToSomeButton(myActionListener);

Но я думаю, что это сомнительно, потому что это все еще разоблачает тот факт, что есть кнопка. Проблема может стать более очевидной, когда у вас есть, например, JTextField ввести номер, а затем изменить его на JSlider: Это изменит требуемые типы прослушивателей, хотя это должно быть только вопросом представления.

В приложениях Swing я думаю, что слушатели могут рассматриваться как "маленькие контроллеры". И я думаю, что вполне возможно иметь анонимных слушателей, которые напрямую вызывают методы модели (если только вокруг этих вызовов нет дополнительной логики).

Сказав это: я бы не стал рассматривать пример, который вы указали, как "хороший" пример для MVC. Прежде всего, потому что выбранный пример не показывает ключевую точку MVC: модель не содержит состояния, и тот факт, что модель в MVC обычно является той вещью, которую необходимо соблюдать (и, таким образом, как прикрепили слушатели) не становится понятным. И, во-вторых, потому что способ установления связи между GUI и моделью сомнителен из-за упомянутых выше моментов.

Мне понравился пример на http://csis.pace.edu/~bergin/mvc/mvcgui.html. Части этого также могут быть подвергнуты сомнению (например, использование общих классов Observer / Observable), но я думаю, что это хорошо показывает основную идею MVC убедительным образом.


РЕДАКТИРОВАТЬ: нет загрузки этого примера в виде ZIP или около того. Но вы можете просто скопировать и вставить TemperatureModel, TemperatureGUI, FarenheitGUI а также MVCTempConvert в IDE. (Предполагается CelsiusGUI присутствовать. это CelsiusGUI, опущен на веб-сайте, но структурно равен GUI Farenheit. Для первого теста строка, в которой он создается, может быть просто закомментирована).

В этом примере предлагается добавить слушателей TemperatureGUI учебный класс. Фактические слушатели созданы и прикреплены конкретным FarenheitGUI учебный класс. Но это более или менее деталь реализации. Ключевым моментом здесь (который также направлен на исходный вопрос) является то, что Слушатели создаются View, в форме внутренних классов или даже анонимных классов. Эти слушатели напрямую вызывают методы модели. А именно, чтобы установить температуру в Фаренгейте (для графического интерфейса Фаренгейта) или установить температуру в градусах Цельсия (для графического интерфейса Цельсия).

Есть еще некоторые степени свободы. Это не "идеальный" или "универсальный" пример MVC. Но это ИМХО лучше, чем большинство других примеров MVC, которые я нашел до сих пор, потому что он хорошо показывает важные аспекты:

  1. Модель Observable
  2. Вид является Observer
  3. "Контроллеры" (то есть прослушиватели в данном случае) - это анонимные / внутренние классы, которые поддерживаются только представлением и вызывают методы модели.

В более сложной, общей установке нельзя использовать Observable/Observer классы. Вместо этого можно было бы создать выделенных слушателей и, возможно, события для модели. В этом случае это может быть что-то вроде TemperatureChangedListener а также TemperatureChangedEvent, Observable/Observer классы были использованы здесь для краткости, потому что они уже являются частью стандартного API.

Начнем с того, что обратите внимание, что могут быть более сложные случаи применения, в которых идея MVC, которая представлена ​​в этом небольшом примере, должна быть немного расширена. В частности, когда нужно выполнить задачи, которые выходят за рамки простого вызова методов модели. Например, когда представление содержит несколько полей ввода, и эти данные должны быть предварительно обработаны или проверены иным образом, прежде чем они будут переданы в модель. Такие задачи не должны выполняться анонимным слушателем. Вместо этого такие задачи могут быть обобщены в классе, который затем может быть назван "Контроллер". Однако присоединение фактических слушателей к компонентам GUI все еще может быть сделано исключительно с помощью View. В качестве чрезмерно наводящего примера: это может произойти как

// In the view:
someButton.addActionListener(new ActionListener()
{
    @Override
    public void actionPerformed(ActionEvent e)
    {
        String s = someTextField.getText();
        Date d = someFormattedTextField.getDate();
        int i = someSlider.getValue();

        // The controller validates the given input, and
        // eventually calls some methods on the Model,
        // possibly using the given input values
        controller.process(s, i, d);
    }
});

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

Представление: ваше представление должно содержать все ваши j-компоненты с их получателями или установщиками, компонент, связанный с размещением кода, код, связанный с макетом. Я никогда не добавляю слушателей ни к одному из компонентов в классе представления, это только имеет дело с расположением

Модель: модель должна иметь заполнители для хранения ваших данных для вашего представления, например: если у вас есть JTable, модель может содержать массив, который будет хранить данные для вашего JTable

Контроллер = Модель + представление (здесь вы связываете модель с представлением), добавьте сюда всех своих слушателей. Привязайте свои текстовые поля, поля со списком и т. Д., Добавьте свою бизнес-логику на стороне клиента, добавьте слушателей действий для своих кнопок. Здесь вы должны получить доступ к своему представлению. и модель.

Надеюсь это поможет.

Если вы следуете шаблону GRASP контроллера, который предполагает разделение уровней интерфейса и домена, actionPerformed() код остается на уровне интерфейса. Концепция объясняется в книге, но для ясности я привожу фигуру из ресурсов преподавателя книги:

введите описание изображения здесь

Изменить: Как я уже говорил в комментарии выше, если вы реализуете другое представление (например, в мобильном приложении или для добавления распознавания голоса), вы хотите сохранить ту же самую системную операцию, отправленную на уровень вашего домена. Мне нравится понятие уровня представления "распознавание жеста пользователя, который преобразуется в команду контроллера".

Вся логика должна быть размещена в контроллере. Однако строгой линии не существует, внутри представления может быть очень простая логика. Поместите actionListeners в контроллер, а затем ваши кнопки в GUI. Так я обычно делаю. Иногда мне лень, и пусть мои кнопки реализуют actionListeners и позволяют им слушать самих себя, в этом случае я помещаю кнопки в пакет контроллера.

Другие вопросы по тегам