2008-08-31 11 views
2

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

условия следующим образом:

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

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

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

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


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

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

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

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

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

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

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

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

  • соединения
    • Воспроизведение getPlayback()
      • Воспроизведение, пауза, прыгать, текущей дорожки и т.д.
      • Expose воспроизведения состояния изменяется
    • треках getTracks()
      • Track getTrack (ID) и т.д.
      • Expose обновления треков
    • Коллекции getCollection()
      • Нагрузка и манипулируют плейлист или именованные коллекции
      • запросов медиа библиотека
      • Expose обновление коллекции

ответ

2

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

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

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

0

@Staale: Спасибо!

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

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

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

somehandlername(int changes, Track old_track, Track new_track) 

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

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

Может быть что-то вроде

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

возвращающегося Будущего может работать ...

0

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

0

@Staale

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

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

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

0

Мои выводы до сих пор;

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

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); 
} 

Это, как строятся querys. Цепь звонит в ваше сердце. жюри все еще не работает на get(). Есть некоторые недостающие подробности, например, как я должен работать с подстановочными знаками и более расширенными запросами с дизъюнкциями. Мне может просто понадобиться функция завершения обратного вызова, возможно, похожая на Asynchronous Completion Token, но об этом мы узнаем. Возможно, это произойдет в дополнительном слое.

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); 
}