Я разрабатываю приложение java swing, которое будет иметь несколько подсистем. Предположим, что я делаю программу интернет-чата со случайным дополнительным набором функций. Эта функциональность будет ... планировщиком, где вы можете установить время и получить напоминание в то время, а также уведомить всех в списке друзей, что у вас есть напоминание.Шаблон проектирования Java Swing для взаимодействия сложного класса
Имеет смысл организовать эту функциональность в трех классах: графическом интерфейсе, чат-менеджере и планировщике. Эти классы будут делать следующее:
GUI - Определить все компоненты распашных и события
ChatManager - Создание подключения чат, отправлять и получать сообщения, управлять список друзей
Планировщик - система мониторинга время, отправлять уведомления, хранить файл для запоминания событий между сеансами
Для того чтобы программа работала, каждый из этих классов должен быть способен взаимодействовать с двумя другими. GUI должен сообщить ChatManager, когда отправить сообщение и сообщить Планировщику, когда начать мониторинг. ChatManager должен отображать сообщения в графическом интерфейсе, когда они получены, и, наконец, планировщику необходимо как уведомить GUI, когда он будет готов, так и отправить обновление статуса или что-то еще в ChatManager.
Конечно, классы, описанные здесь, все довольно просты, и не может быть плохой идеей просто позволить им общаться друг с другом напрямую. Однако, ради этого вопроса, предположим, что взаимодействия намного сложнее.
Например, допустим, что мы можем зарегистрировать конкретное событие с планировщиком вместо определенного времени. При отправке сообщения я отправил его пользователю, сохранил его в файле журнала, создал объект события и передал его планировщику и обработал любые исключения, которые могут быть отправлены на этом пути.
Когда общение становится таким сложным, становится сложно поддерживать код, если общение с этими классами может происходить во многих разных местах. Например, если бы я должен был реорганизовать ChatManager, мне бы также потребовалось сделать важные chaneges как для графического интерфейса, так и для планировщика (и все остальное, если я представим что-то новое). Это затрудняет поддержание кода и заставляет нас программистов, лишенных сна, с большей вероятностью ввести ошибки при внесении изменений.
Решение, которое изначально казалось наиболее полезным, заключается в использовании шаблона проектирования медиатора. Идея состоит в том, что ни один из этих трех основных классов напрямую не знает друг о друге, и каждый из них знает класс посредника. Класс посредника, в свою очередь, определяет методы, которые обрабатывают связь между тремя классами. Так, например, GUI будет вызывать метод sendMessage() в классе посредника, и медиатор будет обрабатывать все, что должно было произойти. В конечном счете, это отделяет три основных класса, и любые изменения одного из них, скорее всего, приведут только к изменениям медиатора.
Однако это вводит две основные проблемы, которые в конечном итоге привели меня ко мне, чтобы найти обратную связь. Они заключаются в следующем:
Проблемы
Многие задачи необходимо обновить графический интерфейс, но посредник не знает о компонентах. - Предположим, пользователь запускает программу и вводит свое имя пользователя/пароль и нажимает логин для входа на сервер чата. При входе в систему вы хотите сообщить о процессе входа в систему, указав текст на экране входа в систему, например «Подключение ...», «Вход в систему ...» или «Ошибка». Если вы определяете метод входа в класс Mediator, единственным способом отображения этих уведомлений является создание общедоступного метода в классе GUI, который обновляет правильный JLabel. В конце концов, для GUI-класса потребуется очень много методов для обновления его компонентов, таких как отображение сообщения от конкретного пользователя, обновление списка друзей при входе/выходе пользователя в систему и т. Д. Кроме того, вам следует ожидать, что эти обновления графического интерфейса могут случайно произойти в любое время. Все хорошо?
The Swing Event Dispatch Thread. В основном вы вызываете методы посредника из компонента ActionListeners, которые выполняются на EDT. Тем не менее, вы не хотите отправлять сообщения или читать/записывать файлы на EDT, или ваш графический интерфейс становится невосприимчивым. Таким образом, было бы неплохо иметь SingleThreadExecutor, доступный в объекте посредника, с каждым методом в объекте посредника, определяющим новый runnable, который он может передать в поток исполнителей? Кроме того, обновление компонентов GUI должно происходить в EDT, но этот поток Executor будет вызывать методы для обновления компонентов GUI. Ergo, каждый публичный метод в GUI-классе должен был бы подчиниться EDT для выполнения. Это необходимо?
Мне, кажется, что много работы, чтобы иметь метод в классе GUI, чтобы обновить все компоненты, которые каким-то образом взаимодействует с внешним миром, с каждым из этих методов, имеющих дополнительные подслушал проверки, если он находится на EDT и добавление себя в EDT в противном случае. Кроме того, каждый публичный метод класса Mediator должен был бы сделать что-то подобное, добавив код Runnable в поток посредника или запуск рабочего потока.
В целом, похоже, почти так же необходимо поддерживать приложение с шаблоном посредника, чем поддерживать приложение без него. Итак, в этом примере, что бы вы делали иначе, если что?
Разделение графического интерфейса на несколько классов и связь посредника с контроллером графического интерфейса звучит как хорошая идея. Это не уменьшает количество методов, но на самом деле делает его намного более организованным во многих отношениях. API SwingWorker (включенный в JDK6, я считаю), предлагает удобный интерфейс для запуска фоновых потоков и выполнения методов на EDT во время или после жизни потока. Имея использование медиатора, они могут просто уведомлять GUI о завершении определенных долговременных задач. – 2009-06-11 02:26:14
@Jack - точно :) – willcodejavaforfood