2008-08-31 65 views
2

我有暴露异步远程接口的最佳方式的一个问题。暴露远程接口或对象模型

的条件如下:

  • 的协议是异步
  • 第三方可以在任何时间修改数据
  • 命令往返可以显著
  • 该模型应该很好地适用于UI相互作用
  • 该协议支持对某些对象的查询,并因此必须与模型

作为提高我的技能缺乏在这方面的手段(和温习一下在一般的Java),我已经开始project创建一个基于Eclipse的前端为xmms2(如下所述)。

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

我从图案擅用他人名义或具体的例子和补丁:)


通用讨论我在这里的主要目的是了解这个类的一般问题,欢迎任何东西。如果我的项目可以从中获益,那么很好,但我会严格地提出一些建议来开始讨论。

我已经实现了一个协议抽象,我称之为'client'(出于传统原因),它允许我使用方法调用来访问大多数暴露的特性,即使它很不完美,我也很满意。

由XMMS2守护程序提供的功能是一样的东西轨道检索,元数据检索和操纵,改变播放状态,负载的播放列表等等,等等。

我在更新到XMMS2的最新的稳定版本的中间,我想我也可以解决一些我目前的实施明显的弱点。

我的计划是建立在协议接口的上方,一个允许与守护更自然的交互更好的抽象。目前的'model'实现很难使用,坦率地说相当丑陋(更不用说UI代码真的很恐怖了)。

今天我有Tracks接口,我可以用它来获取基于其ID Track类的实例。搜索是通过Collections接口(不幸的命名空间冲突)来实现的,我想这个接口我宁愿移动到Tracks。

任何数据都可以由第三方在任何时间进行修改,这应该被适当地反映在模型中,改变的通知分布式

连接时,通过返回的对象分层结构,看起来像这些接口被暴露此:

  • 连接
    • 播放getPlayback()
      • 播放,暂停,跳步,当前曲目等
      • 揭露回放状态改变
    • 曲目getTracks()
      • 轨道getTrack(ID)等
      • 揭露跟踪更新
    • 收藏夹getCollection()
      • 加载和操作播放列表或指定集合
      • 查询媒体库
      • 揭露收集更新

回答

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)等。

希望这有助于作为一个起点。

0

@Staale:非常感谢!

使用未来进行异步操作很有趣。唯一的缺点是它不提供回调。但后来,我尝试了这种方法,并看看我的地方:)

我目前正在解决一个类似的问题,使用工作线程和阻塞队列调度传入的命令答复,但该方法不翻译得非常好。

远程对象可以修改,但由于我使用线程,我试图保持对象不可变。我现在的假设是,我会送轨道上的更新通知事件的形式,

somehandlername(int changes, Track old_track, Track new_track) 

或相似的,但后来我可能会结束与同一轨道的几个版本。

我一定会考虑Djangos方法链接。我一直在寻找一些类似的构造,但一直没能找到一个好的变体。返回可迭代的东西很有趣,但是查询可能需要一些时间才能完成,而且我不希望在完全构建之前实际执行查询。

也许类似

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

返回一个未来可能的工作...

0

可迭代只有方法迭代得到()或诸如此类。因此,除非实际开始迭代,否则不需要构建任何查询或执行任何代码。它确实使你的例子中的执行是多余的。但是,线程将被锁定,直到第一个结果可用,所以您可能会考虑使用Executor在单独的线程中运行查询的代码。

0

@Staale

这当然是可能的,但是当你注意,这将使它阻塞(在家里像由于睡眠盘10秒),这意味着我不能用它来更新UI直。

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

I我会再考虑一下。我可能只是能够解决一些问题。它的确让代码看起来很漂亮。

0

我到目前为止的结论;

我很在意是否为Track对象使用getters,或者只是暴露成员,因为对象是不可变的。

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