4

我正在升级一个数据与 UI 轻微耦合的设计:

class Object {
    UI * ui;
};


class UI {
    Object * object;
};

通过 UI 指针将更新通知推送到 ui 相当简单,但新的要求是数据与 UI 完全分离,并且不同对象具有多个不同的 UI 表示,因此单个 UI 指针不再这样做,也不是允许成为数据层的一部分。

QObject由于对象数量多(数亿范围内)并且QObject比层次结构中最大的对象大几倍,因此无法使用类似和信号之类的东西。对于 UI 部分,这并不重要,因为一次只有一部分对象是可见的。

我实现了一个 UI 注册表,它使用 multihash 来存储所有 UI,使用Object *为一个键,以便能够获取给定对象的 UI 并发送通知,但是 UI 的查找以及注册和注销存在考虑到高对象数,开销很大。

所以我想知道是否有一些设计模式可以在解耦层之间以更少的开销发送通知?

澄清一下:大多数更改都是在 UI 端完成的,UI 元素保留指向相关对象的指针,所以这不是问题。但是从 UI 端对某些对象进行的一些更改会导致数据层中的相关对象发生无法预测的更改,以便请求更新受影响对象的 UI。事实上,对一个对象的 UI 上的单个更改可能会导致对其他对象的一连串更改,因此我需要能够通知它们最终的 UI 表示进行更新以反映这些更改。

4

2 回答 2

1

一种用于解耦通信的通用机制是发布-订阅模式。在这种情况下,更新的对象会将通知发布到消息队列,然后消息队列负责通知已在队列中注册的 UI 组件有兴趣接受该特定类别的通知。

原则上,这与您已经尝试过的 UI 注册表类似。主要区别在于,要更新的 UI 组件不是纯粹由其引用Object的 s 来标识的,而是由通知类型来标识的。

这允许在特异性和状态保持之间进行权衡:如果模型设置为使得与 an 关联的每个 UI 组件Object obj都会在每次更新时得到通知obj,那么它就相当于 UI 注册表。另一方面,该模型可以安排成当某个子类别Object发布更新时通知某些 UI 组件,然后每个组件可以根据通知的内容检查自己是否需要修改其状态. 极端情况下,每个 UI 对象都可以通过 any 发布的任何消息来通知Object,这相当于全局“更新 UI 状态”方法。

发布-订阅模型既包含这两个极端,也包含介于两者之间的范围,您可以在其中找到合适的折衷方案。

于 2015-03-07T13:39:13.717 回答
0

我设法想出了一个非常有效的解决方案。

我没有使用“UI 注册表”跟踪所有 UI,而是创建了一个Proxy对象并将 UI 注册表替换为代理注册表。

Proxy对象是为具有任何视觉表示的每个对象创建的。它本身扩展QObject并实现了一个接口来访问底层的属性Object,将它们包装在 Qt 样式属性中。

然后,该Proxy对象用作每个属性UI以读取和写入底层Object属性,因此它“自动”适用于可能引用特定代理的每个 UI。

这意味着不需要跟踪每个特定UI的每个Object,而是Proxy简单地通过计算引用它的 UI 的数量来管理 的生命周期。

我还设法通过添加一个位标志(其他标志留下一些空闲位)来消除所有不会产生结果的查找,该hasProxy标志在创建或销毁代理时为每个对象切换。这样,在实际Object的成员中,我可以快速检查对象是否具有代理,而无需在注册表中查找,如果不使用“盲”数据例程,则查找代理并通过它操作对象. 这将注册表查找限制为只有少数会实际得到结果的查找,并消除了大量几乎徒劳的查找,只是为了意识到对象根本没有视觉表示。

简而言之,总结对之前设计的改进:

  • 注册表现在要小得多,从必须存储对象本身的指针和所有关联 UI 的向量我现在减少到 8 个字节Proxy- 指向对象的指针和任意数量关联 UI 的计数器

  • 通知是自动的,只需要通知代理,它会自动通知所有引用它的 UI

  • 以前赋予 UI 的功能现在已移至代理并在所有 UI 之间共享,因此 UI 本身更轻巧且更易于实现,实际上我已经从必须QQuickItem为每种对象类型专门化一个唯一的对象到能够使用通用 QML Item,而无需为 UI 实现和编译任何本机类

  • 我以前必须手动管理的东西,实际的通知和负责它们的对象现在都自动管理了

  • 内存使用和 CPU 周期的开销已大大减少。与原始设计相比,之前的解决方案牺牲了 CPU 时间以减少内存使用量,但新设计消除了大部分 CPU 开销并进一步降低了内存使用量,并且使实现更容易和更快。

就像吃蛋糕一样吃它:)

于 2015-03-07T23:05:40.047 回答