公开远程接口或对象模型

Posted

技术标签:

【中文标题】公开远程接口或对象模型【英文标题】:Exposing a remote interface or object model 【发布时间】:2010-09-07 09:55:17 【问题描述】:

我有一个关于公开异步远程接口的最佳方式的问题。

条件如下:

协议是异步的 第三方可以随时修改数据 命令往返可能很重要 模型应该非常适合 UI 交互 协议支持对特定对象的查询,模型也必须如此

作为提高我在这方面缺乏技能的一种手段(并全面提升我的 Java),我已经启动了一个 project 来为 xmms2 创建一个基于 Eclipse 的前端(如下所述)。

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

我欢迎从一般讨论到模式名称删除或具体示例和补丁的任何内容:)


我在这里的主要目标是了解这类问题。如果我的项目可以从中受益,那很好,但我严格介绍它是为了有一些东西可以开始讨论。

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

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

我正在更新到 xmms2 的最新稳定版本,我想我不妨修复当前实现的一些明显弱点。

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

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

任何数据都可以随时被第三方修改,这应该在模型和分发的变更通知中得到适当的反映

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

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

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

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

【讨论】:

【参考方案3】:

@Staale

当然有可能,但正如您所指出的,这会使其阻塞(由于磁盘休眠而在家中停留大约 10 秒),这意味着我无法使用它直接更新 UI。

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

我会再考虑一下。我可能只是能够解决一些问题。它确实使代码看起来不错。

【讨论】:

【参考方案4】:

@Staale:非常感谢!

将 Future 用于异步操作很有趣。唯一的缺点是它不提供回调。但话又说回来,我尝试了这种方法,看看它把我带到了哪里:)

我目前正在使用工作线程和阻塞队列来分派传入的命令回复来解决类似的问题,但这种方法不能很好地转化。

可以修改远程对象,但由于我使用线程,所以我尽量保持对象不可变。我目前的假设是我将在表单上发送有关轨道更新的通知事件

somehandlername(int changes, Track old_track, Track new_track)

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

我一定会研究 Django 的方法链。我一直在研究一些类似的构造,但一直未能想出一个好的变体。返回一些可迭代的东西很有趣,但是查询可能需要一些时间才能完成,而且我不想在查询完全构造之前实际执行它。

也许是这样的

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

返回 Future 可能会起作用...

【讨论】:

【参考方案5】:

到目前为止我的结论;

对于是否为 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);

【讨论】:

以上是关于公开远程接口或对象模型的主要内容,如果未能解决你的问题,请参考以下文章

如何通过多态关系将关系写入到远程模型

BOM(Browser Object Model 浏览器对象模型)

模型中的 Qt 模型?

文档对象模型操作xml文档

调用 COM 对象

Java数据访问对象模式