代码之家  ›  专栏  ›  技术社区  ›  Henrik Gustafsson

公开远程接口或对象模型

  •  2
  • Henrik Gustafsson  · 技术社区  · 16 年前

    我有一个问题,关于如何最好地公开异步远程接口。

    条件如下:

    • 协议是异步的
    • 第三方可以随时修改数据
    • 命令往返可能很重要
    • 模型应该非常适合于UI交互。
    • 该协议支持对某些对象的查询,因此模型

    作为提高我在这方面缺乏技能的一种方法(一般地说我的Java),我已经开始了 project 为创建基于Eclipse的前端 xmms2 (如下所述)。

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

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


    我在这里的主要目标是了解这类问题。如果我的项目能从中获益,好吧,但是我严格地提出它是为了开始讨论。

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

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

    我正在更新到XMMS2的最新稳定版本,我想我还可以修复当前实现中一些明显的弱点。

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

    今天我有 Tracks 可用于获取的实例的接口 Track 基于其ID的类。通过 Collections 接口(不幸的名称空间冲突),我想我宁愿转移到跟踪。

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

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

    • 连接
      • 播放GetPlayback()
        • 播放、暂停、跳跃、当前曲目等
        • 显示播放状态更改
      • 跟踪gettracks()
        • 跟踪gettrack(id)等
        • 公开跟踪更新
      • 集合getCollection()
        • 加载和操作播放列表或命名集合
        • 查询媒体库
        • 公开集合更新
    5 回复  |  直到 9 年前
        1
  •  2
  •   Mohammad Faisal Reza    9 年前

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

    对于查询,我建议使用方法链接来构建查询对象,方法链接返回的每个对象都应该 Iterable . 类似于Djangos模型。说你有 QuerySet 实现 Iterable<Song> . 然后你可以打电话 allSongs() 它将返回对所有歌曲进行迭代的结果。或 allSongs().artist("Beatles") 你将拥有一首无与伦比的贝特尔之歌。甚至 allSongs().artist("Beatles").years(1965,1967) 等等。

    希望这能作为一个起点。

        2
  •  0
  •   Staale    16 年前

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

        3
  •  0
  •   Community skywinder    7 年前

    @ Staale

    当然有可能,但正如您注意到的,这会使它阻塞(在家里,由于磁盘休眠,大约10秒钟),这意味着我不能直接使用它来更新UI。

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

    我再考虑一下。我也许能解决一些问题。它确实让代码看起来很漂亮。

        4
  •  0
  •   Community skywinder    7 年前

    @ Staale :谢谢大家!

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

    我目前正在使用一个工作线程和一个阻塞队列来调度传入的命令响应来解决类似的问题,但是这种方法不能很好地转换。

    远程对象可以修改,但是因为我确实使用线程,所以我尝试保持对象不变。我目前的假设是,我将在表单上发送跟踪更新的通知事件

    somehandlername(int changes, Track old_track, Track new_track)
    

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

    我一定会研究一下Djangos方法链接。我已经研究过一些类似的结构,但还没有找到一个好的变体。返回一个可ITerable是很有趣的,但是查询可能需要一些时间才能完成,我不想在它完全构造之前实际执行查询。

    也许有点像

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

    回归未来可能有用…

        5
  •  0
  •   Henrik Gustafsson    16 年前

    到目前为止我的结论;

    对于是否对跟踪对象使用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);
    }
    

    这就是如何构建查询。将呼叫链接到您的心内容。尽管如此,陪审团仍在进行辩论。缺少一些细节,例如我应该如何处理通配符和更高级的分离查询。我可能只需要一些完成回调功能,可能类似于 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);
    }