Защитный шаблон с качающейся резьбой - PullRequest
2 голосов
/ 02 июня 2011

Для простоты представьте приложение, которое загружает файл. Существует простой графический интерфейс с одной меткой, которая отображает прогресс. Чтобы избежать нарушений EDT, я, как и каждый законный гражданин, загружаю файл в один поток (основной) и обновляю графический интерфейс пользователя в другом (EDT). Итак, вот соответствующий кусок псевдокода:

class Downloader {
    download() {
        progress.startDownload();
        while(hasMoreChunks()) {
            downloadChunk();
            progress.downloadedBytes(n);
        }
        progress.finishDownload();
    }
}

class ProgressDisplay extends JPanel {
    JLabel label;
    startDownload() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                label.setText("Download started");
            }
        });
    }
    downloadedBytes(int n) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                label.setText("Downloaded bytes: " + n);
            }
        });
    }
    finishDownload() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                label.setText("Download finished");
            }
        });
    }
}

Мне нравится тот факт, что Java не поддерживает замыкания, а код мне кристально понятен. Шутки в сторону, мне интересно ... Я делаю это неправильно? Можно ли устранить все эти уродливые шаблоны с помощью SwingUtilities, анонимную реализацию Runnable в каждом методе и т. Д .?

Мой случай немного сложнее, чем этот, но я стараюсь не переусердствовать, используя прокси или что-то в этом роде.

Ответы [ 5 ]

2 голосов
/ 02 июня 2011

Для этих задач есть специальный класс: SwingWorker. Это позволяет вам запускать код в отдельном потоке и обновлять пользовательский интерфейс в конце работы.

1 голос
/ 02 июня 2011

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

public abstract static class SwingTask implements Runnable
{
    public void start()
    {
         SwingUtilities.invokeLater( this );
    }
}

startDownload() {
    new SwingTask() { public void run() {
        label.setText("Download started");
    } }.start();
}

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

0 голосов
/ 02 июня 2011

Просто необработанная идея:

public class ThreadSafeJLabel extends JLabel {

    @Override
    public void setText(final String text) {
        if (SwingUtilities.isEventDispatchThread()) {
            super.setText(text);
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    setText(text);
                }
            });
        }
    }

}

Не пробовал, но, думаю, при вызове setText() из EDT будет использоваться super;но когда задействован другой поток, переопределенный (ThreadSafeJLabel.setText()) будет вызван позже, на этот раз внутри EDT.

0 голосов
/ 02 июня 2011

Мы использовали Spin в проекте несколько лет назад.

0 голосов
/ 02 июня 2011

Я бы использовал вспомогательный метод, например.

 public void setLabelText(final JLabel label, final String text) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            label.setText(text);
        }
    });
 }
...