Предоставление удаленного интерфейса или объектной модели

У меня вопрос о том, как лучше всего раскрыть асинхронный удаленный интерфейс.

Условия следующие:

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

Чтобы улучшить мои недостающие навыки в этой области (и освежить мою Java в целом), я запустил проект, чтобы создать интерфейс на основе Eclipse для xmms2 (описанный ниже).

Итак, вопрос: как мне представить удаленный интерфейс как аккуратную модель данных (в данном случае отслеживать управление и обработку событий)?

Я приветствую все, от общих обсуждений до именования шаблонов или конкретных примеров и исправлений :)


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

Я реализовал абстракцию протокола, которую я называю 'клиент' (по устаревшим причинам), которая позволяет мне получать доступ к наиболее уязвимым функциям с помощью вызовов методов, которые меня устраивают, даже если они далеко не идеальны.

Функции, предоставляемые демоном xmms2, - это такие вещи, как поиск треков, извлечение метаданных и манипулирование ими, изменение состояния воспроизведения, загрузка списков воспроизведения и т. д. И т. Д.

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

Мой план состоит в том, чтобы создать лучшую абстракцию поверх интерфейса протокола, которая позволит более естественное взаимодействие с демоном. Текущая реализация 'модель' сложна в использовании и, откровенно говоря, довольно уродлива (не говоря уже о UI-коде, который действительно ужасен).

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

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

Эти интерфейсы открываются при подключении, возвращая иерархию объектов, которая выглядит следующим образом:

  • Связь
    • Воспроизведение getPlayback ()
      • Воспроизведение, пауза, прыжок, текущий трек и т. д.
      • Показать изменения состояния воспроизведения
    • Отслеживает getTracks ()
      • Отслеживать getTrack (id) и т. д.
      • Выставлять обновления треков
    • Коллекции getCollection ()
      • Загружать и управлять плейлистами или именованными коллекциями
      • Медиа-библиотека запросов
      • Показывать обновления коллекции
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
488
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Что касается асинхронного бита, я бы посоветовал проверить java.util.concurrent и особенно интерфейс Future<T>. Будущий интерфейс используется для представления объектов, которые еще не готовы, но создаются в отдельном потоке. Вы говорите, что объекты могут быть изменены в любое время третьей стороной, но я все же предлагаю вам использовать здесь неизменяемые возвращаемые объекты и вместо этого иметь отдельный журнал потоков / событий, на который вы можете подписаться, чтобы вас заметили, когда истекает срок действия объектов. Я мало занимаюсь программированием с пользовательским интерфейсом, но я считаю, что использование Futures для асинхронных вызовов позволит вам иметь гибкий графический интерфейс, а не тот, который ждал ответа от сервера.

Для запросов я бы предложил использовать цепочку методов для создания объекта запроса, и каждый объект, возвращаемый цепочкой методов, должен быть Iterable. Подобно модели Djangos. Скажем, у вас есть QuerySet, который реализует Iterable<Song>. Затем вы можете вызвать allSongs(), который вернет результат, повторяющийся по всем песням. Или allSongs().artist("Beatles"), и у вас будет итерация для всех песен Betles. Или даже allSongs().artist("Beatles").years(1965,1967) и тд.

Надеюсь, это поможет в качестве отправной точки.

@ Staale: Большое спасибо!

Использование Future для асинхронных операций интересно. Единственный недостаток в том, что он не поддерживает обратные вызовы. Но опять же, я попробовал этот подход, и посмотрите, к чему это привело :)

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

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

somehandlername(int changes, Track old_track, Track new_track)

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

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

Возможно что-то вроде

Tracks.allSongs().artist("Beatles").years(1965,1967).execute()

возвращение будущего может сработать ...

Iterable имеет только метод Iterator get () или что-то в этом роде. Поэтому нет необходимости создавать какие-либо запросы или выполнять какой-либо код, пока вы не начнете итерацию. Это делает выполнение в вашем примере избыточным. Однако поток будет заблокирован до тех пор, пока не будет доступен первый результат, поэтому вы можете рассмотреть возможность использования Executor для запуска кода запроса в отдельном потоке.

@ Staale

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

Я мог бы использовать итератор для создания копии результата в отдельном потоке, а затем отправить ее в пользовательский интерфейс, но, хотя решение итератора само по себе довольно элегантно, оно не очень хорошо вписывается. В конце концов, что-то, реализующее IStructuredContentProvider, должно вернуть массив всех объектов, чтобы отобразить его в TableViewer, поэтому, если я смогу получить что-то подобное из обратного вызова ... :)

Я подумаю еще немного. Я мог бы просто что-нибудь придумать. Это придает коду красивый вид.

Мои выводы на данный момент;

Я не понимаю, использовать ли геттеры для объектов Track или просто раскрывать члены, поскольку объект неизменен.

class Track {
    public final String album;
    public final String artist;
    public final String title;
    public final String genre;
    public final String comment;

    public final String cover_id;

    public final long duration;
    public final long bitrate;
    public final long samplerate;
    public final long id;
    public final Date date;

    /* Some more stuff here */
}

Любой, кто хочет знать, когда что-то случилось с треком в библиотеке, реализовал бы это ...

interface TrackUpdateListener {
    void trackUpdate(Track oldTrack, Track newTrack);
}

Так строятся запросы. Звоните по цепочке, сколько душе угодно. однако жюри по поводу get () еще не принято. Отсутствуют некоторые детали, например, как я должен обрабатывать подстановочные знаки и более сложные запросы с дизъюнкциями. Мне может просто понадобиться некоторая функция обратного вызова завершения, возможно, аналогичная Токен асинхронного завершения, но мы посмотрим на это. Возможно, это произойдет в дополнительном слое.

interface TrackQuery extends Iterable<Track> {
    TrackQuery years(int from, int to);
    TrackQuery artist(String name);
    TrackQuery album(String name);
    TrackQuery id(long id);
    TrackQuery ids(long id[]);

    Future<Track[]> get();
}

Некоторые примеры:

tracks.allTracks();
tracks.allTracks().artist("Front 242").album("Tyranny (For You)");

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

interface Tracks {
    TrackQuery allTracks();

    void addUpdateListener(TrackUpdateListener listener);
    void removeUpdateListener(TrackUpdateListener listener);
}

Другие вопросы по теме