Java - Swing UI отдельная логика сокетов - PullRequest
0 голосов
/ 27 июня 2018

Я впадаю в отчаяние, я разрабатываю простой многопользовательский чат на Java на основе принципа клиент-сервер. Я уже написал простое многопоточное серверное приложение, и оно прекрасно работает. Моя проблема - клиент на основе Swing GUI Toolkit. Базовый интерфейс с циклом выполнения для получения сообщений в фоновом режиме. Моя проблема в том, что я хочу отделить логику сокета от пользовательского интерфейса, это означает, что в лучшем случае у меня есть два разных класса: один для цикла выполнения сокета, а другой - для управления пользовательским интерфейсом. Из-за проблемы, что цикл выполнения должен уведомлять / добавлять сообщения в пользовательский интерфейс, они зависят друг от друга.

  • MessengerView - это мой основной класс, который содержит интерфейс Swing и все зависимые компоненты. На данный момент этот класс содержит также логику сокетов, но я хочу извлечь их из внешнего класса.
  • ClientRuntime класс, который должен содержать логику сокета ...

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

addListener(MessageArrivedListener listener);
emitMessageArrivedEvent(String message);

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

В свое время, когда я работал с C ++, для этой проблемы я иногда использовал Friend-классы, потому что это позволяет получить доступ к членам класса других классов! Но это решение часто очень сбивает с толку, и я не нашел такой возможности для Java.

Так есть ли какие-либо другие возможности для удержания соединения между свинг-виджетами и логикой сокета, не сохраняя их в одном классе (файле)?

Ответы [ 2 ]

0 голосов
/ 27 июня 2018

how could I separate them and how could I connect them?

Подключите их с помощью BlockingQueue - это первый выбор при выборе способов подключения потоков.

ClientRuntime класс должен запустить 2 потока: один принимает запросы из очереди блокировки и отправляет их на сервер, а второй постоянно читает сообщения с сервера через сокет и отправляет их в поток пользовательского интерфейса. Поток пользовательского интерфейса уже имеет входную очередь блокировки для сообщений: к ней обращается SwingUtilities.invokeLater(Runnable);. Класс ClientRuntime не обращается напрямую к очереди пользовательского интерфейса: он вызывает метод из MessengerView и передает то, что он получил из сокета, двоичный массив или строку json, и этот метод пользовательского интерфейса преобразует его в некоторый Runnable, который фактически обновляет пользовательский интерфейс.

0 голосов
/ 27 июня 2018

они зависят друг от друга

Ну, на самом деле это не так. Слой «сокета» заботится только о том, чтобы он был запущен, запущен, опубликовал несколько сообщений и остановился.

Как все, что на самом деле делается / обрабатывается, его не волнует, он просто «запускается», когда ему говорят, обрабатывает входные / выходные сообщения, публикует уведомления и «останавливается», когда их просят.

Это в основном модель наблюдателя или, если хотите, модель производителя / потребителя.

Таким образом, уровень сокетов должен определить «протокол» поведения или контракта, с которым он готов работать. Часть этого контракта будет заключаться в том, «как» он генерирует уведомления о новых сообщениях либо через наблюдателя, либо, возможно, через блокировку / очередь только для чтения - решать вам.

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

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

.

Может начинаться с Параллельность в Swing и Рабочие потоки и SwingWorker

Мой вопрос: как я могу их разделить и как я могу их соединить? Например, я пробовал подобные событиям с регистрацией таких методов: Проблема в том, что это очень запутанно, если количество событий увеличивается!

Я так не думаю (ИМХО). То, что вы хотите сделать, это сосредоточиться на «классе» событий. Например, из вышесказанного у вас есть события «жизненного цикла», и у вас есть события «сообщения». Я бы начал с того, что разбил их на два отдельных интерфейса, поскольку те, кто интересуется событиями «сообщения», вероятно, не так заинтересованы в событиях «жизненного цикла», так как вы можете разделить наблюдателей на части.

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

...