2009-06-09 2 views
1

Я разрабатываю приложение java swing, которое будет иметь несколько подсистем. Предположим, что я делаю программу интернет-чата со случайным дополнительным набором функций. Эта функциональность будет ... планировщиком, где вы можете установить время и получить напоминание в то время, а также уведомить всех в списке друзей, что у вас есть напоминание.Шаблон проектирования Java Swing для взаимодействия сложного класса

Имеет смысл организовать эту функциональность в трех классах: графическом интерфейсе, чат-менеджере и планировщике. Эти классы будут делать следующее:

GUI - Определить все компоненты распашных и события
ChatManager - Создание подключения чат, отправлять и получать сообщения, управлять список друзей
Планировщик - система мониторинга время, отправлять уведомления, хранить файл для запоминания событий между сеансами

Для того чтобы программа работала, каждый из этих классов должен быть способен взаимодействовать с двумя другими. GUI должен сообщить ChatManager, когда отправить сообщение и сообщить Планировщику, когда начать мониторинг. ChatManager должен отображать сообщения в графическом интерфейсе, когда они получены, и, наконец, планировщику необходимо как уведомить GUI, когда он будет готов, так и отправить обновление статуса или что-то еще в ChatManager.

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

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

Когда общение становится таким сложным, становится сложно поддерживать код, если общение с этими классами может происходить во многих разных местах. Например, если бы я должен был реорганизовать ChatManager, мне бы также потребовалось сделать важные chaneges как для графического интерфейса, так и для планировщика (и все остальное, если я представим что-то новое). Это затрудняет поддержание кода и заставляет нас программистов, лишенных сна, с большей вероятностью ввести ошибки при внесении изменений.

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

Однако это вводит две основные проблемы, которые в конечном итоге привели меня ко мне, чтобы найти обратную связь. Они заключаются в следующем:

Проблемы

  1. Многие задачи необходимо обновить графический интерфейс, но посредник не знает о компонентах. - Предположим, пользователь запускает программу и вводит свое имя пользователя/пароль и нажимает логин для входа на сервер чата. При входе в систему вы хотите сообщить о процессе входа в систему, указав текст на экране входа в систему, например «Подключение ...», «Вход в систему ...» или «Ошибка». Если вы определяете метод входа в класс Mediator, единственным способом отображения этих уведомлений является создание общедоступного метода в классе GUI, который обновляет правильный JLabel. В конце концов, для GUI-класса потребуется очень много методов для обновления его компонентов, таких как отображение сообщения от конкретного пользователя, обновление списка друзей при входе/выходе пользователя в систему и т. Д. Кроме того, вам следует ожидать, что эти обновления графического интерфейса могут случайно произойти в любое время. Все хорошо?

  2. The Swing Event Dispatch Thread. В основном вы вызываете методы посредника из компонента ActionListeners, которые выполняются на EDT. Тем не менее, вы не хотите отправлять сообщения или читать/записывать файлы на EDT, или ваш графический интерфейс становится невосприимчивым. Таким образом, было бы неплохо иметь SingleThreadExecutor, доступный в объекте посредника, с каждым методом в объекте посредника, определяющим новый runnable, который он может передать в поток исполнителей? Кроме того, обновление компонентов GUI должно происходить в EDT, но этот поток Executor будет вызывать методы для обновления компонентов GUI. Ergo, каждый публичный метод в GUI-классе должен был бы подчиниться EDT для выполнения. Это необходимо?

Мне, кажется, что много работы, чтобы иметь метод в классе GUI, чтобы обновить все компоненты, которые каким-то образом взаимодействует с внешним миром, с каждым из этих методов, имеющих дополнительные подслушал проверки, если он находится на EDT и добавление себя в EDT в противном случае. Кроме того, каждый публичный метод класса Mediator должен был бы сделать что-то подобное, добавив код Runnable в поток посредника или запуск рабочего потока.

В целом, похоже, почти так же необходимо поддерживать приложение с шаблоном посредника, чем поддерживать приложение без него. Итак, в этом примере, что бы вы делали иначе, если что?

ответ

1

Здесь частичный ответ вы проектируете вопросы ...

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

ChatManager и Планировщик будет генерировать UpdateUIMessage.

И я хотел бы написать свой графический интерфейс таким образом

public class MyView { 

    public void handleUpdateMessage(final UpdateUIMessage msg){ 
     Runnable doRun = new Runnable(){ 
      public void run(){ 
       handleMessageOnEventDispatcherThread(msg); 
      } 
     }; 
     if(SwingUtilities.isEventDispatcherThread()){ 
      doRun.run(); 
     } else { 
      SwingUtilities.invokeLater(doRun); 
     } 
    } 
} 

Так у вас есть только один публичный метод вашего GUI, который обрабатывает все вещи EdT.

Если вы хотите иметь свободную связь между графическим интерфейсом и другими компонентами (то есть: вы не хотите, чтобы GUI знал все API других компонентов), GuiController также мог публиковать ActionMessage (на конкретном потоке ?), который будет отправлен посредником другим компонентам.

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

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

  2. Вместо того, чтобы каждый метод в вашем графическом интерфейсе создавал Runnable и использовал SwingUtilities.invokeLater для размещения этого обновления в EDT, я бы посоветовал вам попробовать другое решение. Для моих личных проектов я использую Swing Application Framework (JSR296), который имеет несколько классов Task для запуска фоновых заданий, а затем метод «Успешность» автоматически используется в потоке EDT. Если вы не можете использовать это, вы должны попытаться создать свою собственную аналогичную структуру для фоновых заданий.

+0

Разделение графического интерфейса на несколько классов и связь посредника с контроллером графического интерфейса звучит как хорошая идея. Это не уменьшает количество методов, но на самом деле делает его намного более организованным во многих отношениях. API SwingWorker (включенный в JDK6, я считаю), предлагает удобный интерфейс для запуска фоновых потоков и выполнения методов на EDT во время или после жизни потока. Имея использование медиатора, они могут просто уведомлять GUI о завершении определенных долговременных задач. – 2009-06-11 02:26:14

+0

@Jack - точно :) – willcodejavaforfood

1

Ну, я изменю мир, с которым вы работаете. У вас есть 3 класса, и каждый из них является просто наблюдателем в мире чата. MVC - это способ решения вашей проблемы. Вам нужно было создать модель для своего мира, в этом случае чат-программу. Эта модель будет хранить данные, чат-очереди, список друзей и следить за согласованностью и уведомлять всех об изменениях. Кроме того, будет несколько наблюдателей, которые заинтересованы в состоянии мира и отражают свое состояние для пользователя и сервера. Графический интерфейс обеспечивает визуализацию в очереди друзей и сообщений и реагирует на их изменения. Планировщик рассматривает изменения в запланированных задачах и обновляет модель с их результатами. ChatManager будет лучше выполнять свою работу в нескольких классах, таких как SessionManager, MessageDispatcher, MessageAcceptor и т. Д. У вас есть 3 класса с пустым центром. Создайте центр и соедините их вместе, используя этот центр, и Observer Pattern. Тогда каждый класс будет заниматься только одним классом и только с интересными событиями. Один класс GUI - плохая идея. Разделите на большее количество подклассов, представляющих логическую группу (вид модели). Вот как победить ваш пользовательский интерфейс.

+0

MVC - отличный образец. В частности, я думаю, что это было бы разумным выбором дизайна при работе с несколькими классами GUI. Однако графический интерфейс не просто наблюдает за чатом, он также может наблюдать за планировщиком и любым количеством других классов. Кроме того, эти классы наблюдают за графическим интерфейсом и друг с другом. Это много разных наблюдателей, которых вы должны были создать. Вы также можете взять командный подход и инкапсулировать все события в общий объект, но разбор, который может сильно повлиять на производительность. – 2009-06-11 02:29:15

+0

Ну, я вижу, вы не поняли моего ответа. Реальная власть делает модель, которая удерживает состояние мира. А затем наблюдатели только для этой модели и контроллеры, которые изменяют состояние модели. Вы уменьшаете свое взаимодействие между классами до Controller/Observer -> Model. Нет необходимости создавать взаимодействия между C1/O1 <-> C2/O2. –

1

Возможно, вам захочется взглянуть на проект, который первоначально был запущен как среда MVC для разработки Flex. В то же время PureMVC был перенесен на многие языки программирования, включая Java. Хотя это только в альфа-статусе написания этого!