公开远程接口或对象模型

时间:2008-08-31 19:21:27

标签: java eclipse osgi oop

我对暴露异步远程接口的最佳方式有疑问。

条件如下:

  • 协议是异步的
  • 第三方可以随时修改数据
  • 命令往返可能很重要
  • 该模型应该非常适合用户界面互动
  • 协议支持对某些对象的查询,模型也必须支持

作为一种提高我在这方面缺乏技能的方法(并且通常会刷新我的Java),我已经开始projectxmms2创建一个基于Eclipse的前端(描述)下文)。

所以,问题是;我应该如何将远程接口公开为一个整洁的数据模型(在这种情况下,跟踪管理和事件处理)?

我欢迎从通用讨论到模式名称删除或具体示例和补丁的任何事情:)


我的主要目标是了解这类问题。如果我的项目可以从中获益,那很好,但我严格地提出它来开始讨论。

我实现了一个协议抽象,我称之为'client'(由于遗留原因),它允许我使用方法调用访问大多数公开的功能,即使它远非完美,我也很满意。

xmms2守护程序提供的功能包括跟踪搜索,元数据检索和操作,更改回放状态,加载播放列表等等。

我正在更新到xmms2的最新稳定版本,我想我还可以解决当前实现的一些明显缺点。

我的计划是在协议接口之上构建一个更好的抽象,允许与守护进程进行更自然的交互。当前的'model'实现很难使用,坦率地说非常难看(更不用说真正可怕的UI代码了)。

今天我有Tracks接口,我可以根据它们的id来获取Track类的实例。搜索是通过Collections接口(不幸的命名空间冲突)进行的,我认为我宁愿转移到Tracks。

任何时候都可以由第三方修改任何数据,这应该在模型和分发的变更通知中正确反映

通过返回如下所示的对象层次结构,在连接时公开这些接口:

  • 连接
    • 播放getPlayback()
      • 播放,暂停,跳转,当前曲目等
      • 公开播放状态更改
    • 跟踪getTracks()
      • 跟踪getTrack(id)等
      • 公开跟踪更新
    • 集合getCollection()
      • 加载和操作播放列表或命名集合
      • 查询媒体库
      • 公开收集更新

5 个答案:

答案 0 :(得分:2)

对于异步位,我建议检查java.util.concurrent,尤其是Future<T>接口。将来的接口用于表示尚未准备好的对象,但是在单独的线程中创建。你说对象可以在任何时候被第三方修改,但我仍然建议你在这里使用不可变的返回对象,而是有一个单独的线程/事件日志,你可以订阅它以在对象到期时引起注意。我很少使用UI编程,但我相信使用Futures进行异步调用可以让你拥有一个响应式GUI,而不是等待服务器回复的GUI。

对于查询,我建议使用方法链来构建查询对象,方法链返回的每个对象都应该是Iterable。类似于Djangos模型。假设您QuerySet实施了Iterable<Song>。然后,您可以调用allSongs(),这将返回迭代所有歌曲的结果。或者allSongs().artist("Beatles"),你将拥有所有Betles歌曲的可迭代。甚至是allSongs().artist("Beatles").years(1965,1967)等等。

希望这有助于作为起点。

答案 1 :(得分:0)

@ Staale:非常感谢!

将Future用于异步操作很有意思。唯一的缺点是它不提供回调。但话说回来,我尝试了这种方法,看看那些让我感到高兴的地方:)

我目前正在使用工作线程和阻塞队列来解决传入的命令回复问题,但是这种方法并没有很好地转换。

可以修改远程对象,但由于我确实使用了线程,因此我尝试保持对象不可变。我目前的假设是,我将在表格上发送有关曲目更新的通知事件

somehandlername(int changes, Track old_track, Track new_track)

或类似,但后来我可能最终得到同一曲目的几个版本。

我肯定会研究Djangos方法链。我一直在寻找一些类似的结构,但还没有能够提出一个好的变体。返回可迭代的东西很有意思,但查询可能需要一些时间才能完成,我不想在完全构造之前实际执行查询。

也许像

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

返回未来可能会有效......

答案 2 :(得分:0)

Iterable只有Iterator get()或其他方法。因此,在实际开始迭代之前,不需要构建任何查询或执行任何代码。它确实使您示例中的执行变得多余。但是,在第一个结果可用之前,线程将被锁定,因此您可以考虑使用Executor在单独的线程中运行查询代码。

答案 3 :(得分:0)

@ Staale

当然可能,但是正如你所注意到的那样,这会阻止它(因为睡眠磁盘而在家里等10秒钟),这意味着我不能用它来直接更新UI。

我可以使用迭代器在单独的线程中创建结果的副本,然后将其发送到UI,但迭代器解决方案本身相当优雅,但它不适合。最后,实现IStructuredContentProvider的东西需要返回一个所有对象的数组,以便在TableViewer中显示它,所以如果我可以通过回调获得类似的东西。 。:)

我会多考虑一下。我可能只是能够找到一些东西。它确实给代码一个漂亮的外观。

答案 4 :(得分:0)

到目前为止我的结论;

由于对象是不可变的,我对是否为Track对象使用getter或仅暴露成员感到很遗憾。

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()上。缺少一些细节,例如我应该如何处理通配符以及带有析取的更高级查询。我可能只需要一些完成回调功能,可能类似于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);
}