リモートインターフェイスまたはオブジェクトモデルを公開する
質問
非同期リモートインターフェイスを公開する最良の方法について質問があります。
条件は次のとおりです。
- プロトコルは非同期です
- 第三者がいつでもデータを変更できる
- コマンドのラウンドトリップは重要になる可能性があります
- モデルは UI インタラクションに適している必要があります
- このプロトコルは特定のオブジェクトに対するクエリをサポートしているため、モデルも同様にサポートする必要があります。
この分野で不足しているスキルを向上させる (そして Java 全体をブラッシュアップする) 手段として、私は プロジェクト Eclipse ベースのフロントエンドを作成するには xmms2 (以下で説明します)。
そこで、問題は次のとおりです。リモート インターフェイスをきちんとしたデータ モデル (この場合はトラック管理とイベント処理) として公開するにはどうすればよいですか?
一般的な議論から、パターンの名前の削除、具体的な例やパッチまで、何でも歓迎します :)
ここでの私の主な目標は、この種の問題全般について学ぶことです。私のプロジェクトがそこから利益を得られるのであれば、それで構いませんが、私は厳密に議論のきっかけを得るためにそれを提示しています。
と呼ぶプロトコル抽象化を実装しました 'クライアント' (レガシーな理由により) これにより、メソッド呼び出しを使用して公開されているほとんどの機能にアクセスできるようになり、完璧とは程遠いとしても満足しています。
xmms2 デーモンによって提供される機能には、トラック検索、メタデータの取得と操作、再生状態の変更、プレイリストのロードなどがあります。
私は xmms2 の最新の安定リリースに更新している最中ですが、現在の実装の明らかな弱点のいくつかを修正した方がよいのではないかと考えました。
私の計画は、プロトコル インターフェイス上に、デーモンとのより自然な対話を可能にする、より優れた抽象化を構築することです。現在 'モデル' 実装は使いにくく、率直に言って非常に醜いです (本当に恐ろしい UI コードは言うまでもありません)。
今日私が持っているのは、 トラック インスタンスを取得するために使用できるインターフェイス 追跡 IDに基づいたクラス。検索は次の方法で実行されます。 コレクション インターフェイス (残念ながら名前空間の衝突) があるため、Tracks に移行したほうがよいと思います。
データはいつでも第三者によって変更される可能性があり、これはモデルに適切に反映され、変更通知が配布される必要があります。
これらのインターフェイスは、接続時に次のようなオブジェクト階層を返すことによって公開されます。
- 繋がり
- 再生 getPlayback()
- 再生、一時停止、ジャンプ、現在のトラックなど
- 再生状態の変化を公開する
- トラック getTracks()
- トラック getTrack(id) など
- トラックの更新を公開する
- コレクション getCollection()
- プレイリストまたは名前付きコレクションをロードして操作する
- メディアライブラリのクエリ
- コレクションの更新を公開する
- 再生 getPlayback()
解決
非同期ビットについては、チェックインすることをお勧めします java.util.concurrent
, 、特に Future<T>
インターフェース。future インターフェイスは、まだ準備ができていないが、別のスレッドで作成されているオブジェクトを表すために使用されます。オブジェクトはいつでもサードパーティによって変更できるとおっしゃっていますが、ここでは不変の戻りオブジェクトを使用し、代わりにオブジェクトの有効期限が切れたときに通知されるようにサブスクライブできる別のスレッド/イベント ログを用意することをお勧めします。私は UI に関するプログラミングはほとんどありませんが、Future を非同期呼び出しに使用すると、サーバーの応答を待つ GUI ではなく、応答性の高い GUI が得られると思います。
クエリについては、メソッド チェーンを使用してクエリ オブジェクトを構築することをお勧めします。メソッド チェーンによって返される各オブジェクトは次のようになります。 Iterable
. 。Djangos モデルの仕組みと似ています。持っていると言ってください QuerySet
実装するもの Iterable<Song>
. 。その後、電話をかけることができます allSongs()
これは、すべての曲を反復して結果を返します。または allSongs().artist("Beatles")
, とすると、すべての Betles 曲を反復可能になります。あるいは allSongs().artist("Beatles").years(1965,1967)
等々。
これが出発点として役立つことを願っています。
他のヒント
Iterable には Iterator get() などのメソッドしかありません。したがって、実際に反復を開始するまで、クエリを作成したりコードを実行したりする必要はありません。あなたの例の実行は冗長になります。ただし、最初の結果が得られるまでスレッドはロックされるため、Executor を使用してクエリのコードを別のスレッドで実行することを検討することもできます。
@スターレ
確かにその可能性はありますが、ご指摘のとおり、それによりブロックされてしまいます(ディスクがスリープしているため、自宅では10秒ほど)。つまり、UIを直接更新するために使用できません。
イテレータを使用して別のスレッドで結果のコピーを作成し、それを UI に送信することもできますが、イテレータ ソリューション自体はかなり洗練されていますが、あまりうまく適合しません。最終的には、何かを実装する I構造化コンテンツプロバイダー オブジェクトを表示するには、すべてのオブジェクトの配列を返す必要があります。 テーブルビューア, なので、コールバックからそのようなものを取得できれば...:)
もう少し考えてみます。何かを解決できるかもしれない。コードに見栄えの良いものを与えます。
@スターレ:本当にありがとう!
非同期操作に Future を使用するのは興味深いことです。唯一の欠点は、コールバックを提供しないことです。しかし、もう一度、そのアプローチを試してみました。そして、それがどこにつながったのか見てみましょう:)
私は現在、ワーカースレッドと受信したコマンド応答をディスパッチするためのブロッキングキューを使用して同様の問題を解決していますが、そのアプローチはあまりうまく変換できません。
リモート オブジェクトは変更できますが、スレッドを使用するため、オブジェクトを不変に保つようにしています。私の現在の仮説は、フォームの更新を追跡するときに通知イベントを送信するというものです
somehandlername(int changes, Track old_track, Track new_track)
または同様の方法ですが、その場合、同じトラックの複数のバージョンが作成される可能性があります。
Djangos メソッド チェーンについては必ず検討します。私はいくつかの同様の構造を検討してきましたが、適切なバリアントを思いつくことができませんでした。反復可能なものを返すのは興味深いことですが、クエリが完了するまでに時間がかかる可能性があり、完全に構築される前にクエリを実際に実行したくありません。
おそらく次のようなもの
Tracks.allSongs().artist("Beatles").years(1965,1967).execute()
Future を返すと機能するかもしれません...
これまでの私の結論。
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);
}