Организуйте код для графического интерфейса и ActionListener в Java

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

Написал бы я еще один класс ActionCode, реализующий ActionListener и расширяющий графический интерфейс, или какой путь вы бы предложили и почему?

Что вы можете посоветовать по этому поводу и где я могу найти эти рекомендации? (Кажется, JavaDoc объясняет базовую реализацию ActionListeners, но, похоже, не имеет модели организации больших/средних проектов).


person Sven Fischer    schedule 18.12.2012    source источник
comment
Используйте адаптер, который обеспечивает реализацию методов по умолчанию. Затем предоставьте переопределения там, где требуется другое поведение.   -  person jww    schedule 14.11.2018
comment
Возможный дубликат Реализовать интерфейс и методы переопределения в Java?   -  person jww    schedule 14.11.2018


Ответы (3)


На мой взгляд, не существует «лучшего» подхода. Даже в примерах кода из руководств по sun/oracle используются разные способы реализации слушателей.

По моему опыту, хороший подход:

  • Используйте анонимные реализации: люди знают этот шаблон и быстро узнают его. Это помогает читателю понять код, если есть общий способ делать что-то.
  • Имейте специальный метод, который обрабатывает только слушателей (например, private void addListeners()): Опять же, это помогает всем распознать его и знать, где искать всю логику.
  • Держите слушателей простыми. Это означает менее 5-10 строк кода. Если вам нужна более сложная логика, вызовите метод.
  • Держите количество слушателей небольшим. Если вам нужно> 50 слушателей, вам, вероятно, следует реорганизовать свое представление. Если вам нужно больше 10, вы можете подумать о рефакторинге.

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

И просто упомянем об этом, потому что есть несколько примеров в учебниках по sun/oracle: Старайтесь избегать реализации интерфейса слушателя с самим классом представления. Это может быть нормально, если у вас есть только один слушатель, но в большинстве случаев это ужасно для нескольких событий из нескольких источников с разным поведением.

person tb-    schedule 19.12.2012

Стиль кода может быть делом личного вкуса, но современная литература предполагает, что это нечто большее. Пусть глава о классах в Чистый код покажет вам путь.

Классы должны быть маленькими!

Первое правило занятий — они должны быть небольшими. Второе правило классов заключается в том, что они должны быть меньше этого размера. Нет, мы не будем повторять один и тот же текст из главы «Функции». Но, как и в случае с функциями, при проектировании классов основное правило — меньший размер. Как и в случае с функциями, наш непосредственный вопрос всегда звучит так: «Насколько мал?» С помощью функций мы измеряли размер, считая физические строки. С классами мы используем другую меру. Считаем обязанности...

Принцип единой ответственности (SRP) гласит, что у класса или модуля должна быть одна и только одна причина для изменения. Этот принцип дает нам как определение ответственности, так и рекомендации по размеру класса. У классов должна быть одна обязанность — одна причина для изменения...

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

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

Итак, исходя из этих эвристик, вложенные классы нарушают SRP. Они почти никогда не должны происходить. Вместо этого пусть ваши классы GUI включают члены экземпляров ActionListeners, которые они регистрируют. Держите прослушиватели в отдельном пакете *.listener. Используйте интерфейсы, чтобы сделать их заменяемыми (паттерн стратегии) ​​везде, где это считается эффективным.

person Visionary Software Solutions    schedule 19.12.2012

Java и Swing поддерживают множество небольших объектов, таких как Listeners и Actions. Это много шаблонов, но так устроена Java. В борьбе с ним мало пользы.

Создание встроенных анонимных слушателей довольно безболезненно:

JButton okButton = new JButton("OK");
okButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        ok();
    }
});

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

import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;


public class MainWindow extends JFrame {

    ZapAction zapAction;

    public MainWindow() {

        setSize(new Dimension(200,200));

        zapAction = new ZapAction();

        JMenuBar menuBar = new JMenuBar();
        JMenu menu = new JMenu("Foo");
        menu.add(new JMenuItem(zapAction));
        menuBar.add(menu);
        setJMenuBar(menuBar);

        JButton zapButton = new JButton(zapAction);

        add(zapButton);
    }

    public void zap() {
        // do some zapping
        System.out.println("Zap!");
        // maybe we're all done zapping?
        zapAction.setEnabled(isZappingPossible());
    }

    public boolean isZappingPossible() {
        // determine if there's zapping to be done
        return Math.random() < 0.9;
    }

    class ZapAction extends AbstractAction {
        public ZapAction() {
            super("Zap");
            putValue(AbstractAction.SHORT_DESCRIPTION, "Zap something");
            putValue(AbstractAction.ACCELERATOR_KEY, KeyStroke.getKeyStroke(
                KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
            putValue(AbstractAction.SMALL_ICON, 
                new ImageIcon(MainWindow.class.getResource("/icons/zap.png")));
            setEnabled(true);
        }
        public void actionPerformed(ActionEvent e) {
            zap();
        }
    }
}

public class Zap {
    public static void main(String[] args) {
        MainWindow mainWindow = new MainWindow();
        mainWindow.setVisible(true);
    }
}

Здесь ZapAction — видимость для пакета. Я поместил весь код пользовательского интерфейса в отдельный пакет (скажем, org.myorg.myproject.ui). Таким образом, все объекты пользовательского интерфейса имеют доступ к действиям.

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

person cbare    schedule 19.12.2012