Предоставление удаленного интерфейса или объектной модели - PullRequest
2 голосов
/ 31 августа 2008

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

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

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

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

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

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


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

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

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

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

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

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

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

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

  • Подключение
    • Воспроизведение getPlayback ()
      • Воспроизведение, пауза, прыжок, текущий трек и т. Д.
      • Отображение изменений состояния воспроизведения
    • Треки getTracks ()
      • Отслеживание getTrack (id) и т. Д.
      • Выставлять обновления треков
    • Коллекции getCollection ()
      • Загрузка и управление списками воспроизведения или именованными коллекциями
      • Запрос библиотеки мультимедиа
      • Выставлять обновления коллекции

Ответы [ 5 ]

2 голосов
/ 01 сентября 2008

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

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

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

0 голосов
/ 01 сентября 2008

Мои выводы пока

Меня беспокоит, использовать ли геттеры для объектов 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)");

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

interface Tracks {
    TrackQuery allTracks();

    void addUpdateListener(TrackUpdateListener listener);
    void removeUpdateListener(TrackUpdateListener listener);
}
0 голосов
/ 01 сентября 2008

@ Staale

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

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

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

0 голосов
/ 01 сентября 2008

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

0 голосов
/ 01 сентября 2008

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

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

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

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

somehandlername(int changes, Track old_track, Track new_track)

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...