OOA&D/Java/Software Architecture — рекомендации по структурированию кода обработки событий, чтобы избежать сложного потока данных.

Я реализовал парадигму производителя/потребителя с брокером сообщений в Spring, и мои производители используют WebSocket для извлечения и публикации данных в очереди.

Таким образом, производитель выглядит примерно так: AcmeProducer.java handler/AcmeWebSocketHandler.java.

и с обработчиком было трудно иметь дело.

Во-первых, в обработчике есть два события: onOpen() onMessage().

onOpen должен отправить сообщение в веб-сокет, чтобы подписаться на определенные каналы. onMessage получает сообщения из веб-сокета и добавляет их в очередь.

onMessage имеет некоторые зависимости от AcmeProducer.java, например, ему нужно знать валютные пары для подписки, ему нужна служба брокера сообщений, а также ObjectMapper (десериализатор json) и служба эталонного тестирования.

Когда сообщения потребляются из очереди, они преобразуются в формат OrderBook.java. Каждый производитель имеет свой собственный формат OrderBook и, следовательно, свой собственный AcmeOrderBook.java.

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

Существуют ли какие-либо методы для преобразования сложного потока данных в нечто легкое для понимания?

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

Если бы этот код не использовал парадигму, управляемую событиями, за потоком данных было бы легко следить.

Наконец, есть ли наилучшая практика структурирования кода при использовании веб-сокетов с onOpen и onMessage? Producer/Consumer — это лучшее, что я смог придумать, но я не хочу разбрасывать классы Acme по разным пакетам. Например, AcmeOrderbook должен быть в потребительском классе, но поскольку он зависит от AcmeProducer.java и AcmeHandler.java, они часто редактируются одновременно, поэтому я собрал их вместе.

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


person Allen Hacks    schedule 15.09.2018    source источник


Ответы (1)


Я решил это с помощью диспетчера сообщений и обработчиков сообщений.

Диспетчер только проверяет, является ли сообщение сообщением с данными (моментальным снимком или обновлением), и передает управление классу обработчика сообщений, который сам проверяет тип сообщения (либо моментальный снимок, либо обновление) и правильно обрабатывает этот тип. Если сообщение не является сообщением данных, а чем-то другим, диспетчер отправит сообщение в зависимости от того, что это такое (некоторое событие).

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

Например, внутри анонимной функции обратного вызова есть только это:

messageDispatcher.dispatchMessage(context);

Другой ключевой подход здесь — использование контекста. MessageDispatcher — это отдельный класс (автоматически подключаемый).

Я разделил книгу заказов на свой каталог внутри каждого производителя.

Ну, все требует знаний обо всем, чтобы решить это элегантно.

Последний шаблон для решения этой проблемы: Java EE использует аннотации для своих конечных точек и функций управления, таких как onOpen, onMessage и т. д. Это также хороший подход, потому что с ним обратный вызов становится невидимым, а onOpen / onMessage автоматически вызываются контейнером (используя аннотацию).

person Allen Hacks    schedule 19.11.2018