在分离的设计层之间高效地传递通知
Efficiently pass notifications between decoupled design layers
我正在升级数据与 UI:
轻微耦合的设计
class Object {
UI * ui;
};
class UI {
Object * object;
};
通过 UI 指针将更新通知推送到 ui 相当简单,但是新的要求 ui 数据完全与 UI 分开并且不同的对象具有多个不同的 UI 表示,因此单个 UI 指针不再适用,也不允许成为数据层的一部分。
不可能使用像 QObject
和信号这样的东西,因为它的开销很大,因为对象计数很高(在数亿范围内)并且 QObject
比它大几倍层次结构中最大的对象。对于 UI 部分并不重要,因为一次只能看到一部分对象。
我实现了一个 UI 注册表,它使用多重哈希来存储所有 UI,使用 Object *
作为键,以便能够获得 UI(s) 对于给定的对象并发送通知,但是 UIs 的查找、注册和注销会带来大量的开销,因为对象数量很多。
所以我想知道是否有一些设计模式可以以更少的开销在解耦层之间发送通知?
澄清:大多数更改是在 UI 端完成的,UI 元素保留指向相关对象的指针,因此这不是问题。但是从 UI 端对某些对象所做的一些更改会导致数据层中相关对象发生更改,这些更改无法预测,无法请求更新受影响对象的 UI。事实上,对一个对象所做的 UI 的单一更改可能会导致对其他对象的一系列更改,因此我需要能够通知它们最终的 UI 表示进行更新以反映这些更改。
一种用于解耦通信的通用机制是 publish-subscribe pattern。在这种情况下,更新的对象将 post 通知消息队列,然后消息队列负责通知已向队列注册的 UI 组件有兴趣接受该特定 class 的通知。
这在原则上类似于您已经尝试过的 UI 注册表。主要区别在于要更新的 UI 组件不完全由其引用的 Object
标识,而是由通知类型标识。
这允许在特异性和状态保持之间进行权衡:如果模型的设置使得与 Object
obj
关联的每个 UI 组件都会在每次更新时得到通知obj
,则相当于UI注册表。另一方面,可以安排模型,以便在 Object
post 的某个子类别更新时通知某些 UI 组件,然后每个组件可以自行检查如果它需要根据通知的内容修改它的状态。极端地说,每个 UI 对象都可以通过任何 Object
编辑的任何消息 post 来通知,这相当于全局 'update-UI-state' 方法。
发布-订阅模型既包含这两个极端,也包含介于两者之间的范围,您可以在其中找到合适的折衷方案。
我想出了一个非常有效的解决方案。
我没有使用 "UI registry" 跟踪所有 UI,而是创建了一个 Proxy
对象并将 UI 注册表替换为代理注册表。
Proxy
对象是为每个具有任何视觉表示的对象创建的。它本身扩展 QObject
并实现一个接口来访问底层 Object
的属性,将它们包装在 Qt 样式属性中。
然后 Proxy
对象被用作每个 UI
的 属性 来读写底层的 Object
属性,所以它有效 "automatically"对于每个可能引用特定代理的 UI。
这意味着不需要为每个 Object
跟踪每个特定的 UI
,相反 Proxy
的生命周期仅通过计算 [=69= 的数量来管理] 引用了它。
我还设法消除了所有不会产生结果的查找,方法是添加一个位 hasProxy
标志(其他标志还剩下一些空闲位),该标志为每个对象切换创建或销毁代理时。这样在实际 Object
的成员中我可以快速检查对象是否有代理而无需在注册表中查找,如果没有使用 "blind" 数据例程,如果有则查找代理并通过它操作对象。这将注册表查找限制为只有少数实际会得到结果的查询,并消除了大量那些几乎是徒劳的,只是为了意识到该对象根本没有视觉表示。
简而言之,总结一下相对于之前设计的改进:
注册表现在要小得多,因为必须存储对象本身的指针和所有关联的 UIs 的向量我现在减少到 8 个字节 Proxy
- 指向对象的指针和任意数量的关联 UIs
的计数器
通知是自动的,只需要通知代理,它会自动通知所有引用它的UI
以前赋予 UI 的功能现在移至代理并在所有 UI 之间共享,因此 UI 本身更轻并且更容易实现,事实上,我已经从必须为每个对象类型专门化一个唯一的 QQuickItem
到能够使用通用 QML Item
而无需实现和编译任何本机 类 对于 UI
我以前必须手动管理的东西,实际的通知和负责它们的对象现在都是自动管理的
内存使用和 CPU 周期的开销已大大减少。与原始设计相比,之前的解决方案牺牲了 CPU 时间以换取更少的内存使用量,但新设计消除了大部分 CPU 开销并进一步减少了内存使用量,并且使实施更加容易和快速。
就像吃蛋糕一样:)
我正在升级数据与 UI:
轻微耦合的设计class Object {
UI * ui;
};
class UI {
Object * object;
};
通过 UI 指针将更新通知推送到 ui 相当简单,但是新的要求 ui 数据完全与 UI 分开并且不同的对象具有多个不同的 UI 表示,因此单个 UI 指针不再适用,也不允许成为数据层的一部分。
不可能使用像 QObject
和信号这样的东西,因为它的开销很大,因为对象计数很高(在数亿范围内)并且 QObject
比它大几倍层次结构中最大的对象。对于 UI 部分并不重要,因为一次只能看到一部分对象。
我实现了一个 UI 注册表,它使用多重哈希来存储所有 UI,使用 Object *
作为键,以便能够获得 UI(s) 对于给定的对象并发送通知,但是 UIs 的查找、注册和注销会带来大量的开销,因为对象数量很多。
所以我想知道是否有一些设计模式可以以更少的开销在解耦层之间发送通知?
澄清:大多数更改是在 UI 端完成的,UI 元素保留指向相关对象的指针,因此这不是问题。但是从 UI 端对某些对象所做的一些更改会导致数据层中相关对象发生更改,这些更改无法预测,无法请求更新受影响对象的 UI。事实上,对一个对象所做的 UI 的单一更改可能会导致对其他对象的一系列更改,因此我需要能够通知它们最终的 UI 表示进行更新以反映这些更改。
一种用于解耦通信的通用机制是 publish-subscribe pattern。在这种情况下,更新的对象将 post 通知消息队列,然后消息队列负责通知已向队列注册的 UI 组件有兴趣接受该特定 class 的通知。
这在原则上类似于您已经尝试过的 UI 注册表。主要区别在于要更新的 UI 组件不完全由其引用的 Object
标识,而是由通知类型标识。
这允许在特异性和状态保持之间进行权衡:如果模型的设置使得与 Object
obj
关联的每个 UI 组件都会在每次更新时得到通知obj
,则相当于UI注册表。另一方面,可以安排模型,以便在 Object
post 的某个子类别更新时通知某些 UI 组件,然后每个组件可以自行检查如果它需要根据通知的内容修改它的状态。极端地说,每个 UI 对象都可以通过任何 Object
编辑的任何消息 post 来通知,这相当于全局 'update-UI-state' 方法。
发布-订阅模型既包含这两个极端,也包含介于两者之间的范围,您可以在其中找到合适的折衷方案。
我想出了一个非常有效的解决方案。
我没有使用 "UI registry" 跟踪所有 UI,而是创建了一个 Proxy
对象并将 UI 注册表替换为代理注册表。
Proxy
对象是为每个具有任何视觉表示的对象创建的。它本身扩展 QObject
并实现一个接口来访问底层 Object
的属性,将它们包装在 Qt 样式属性中。
然后 Proxy
对象被用作每个 UI
的 属性 来读写底层的 Object
属性,所以它有效 "automatically"对于每个可能引用特定代理的 UI。
这意味着不需要为每个 Object
跟踪每个特定的 UI
,相反 Proxy
的生命周期仅通过计算 [=69= 的数量来管理] 引用了它。
我还设法消除了所有不会产生结果的查找,方法是添加一个位 hasProxy
标志(其他标志还剩下一些空闲位),该标志为每个对象切换创建或销毁代理时。这样在实际 Object
的成员中我可以快速检查对象是否有代理而无需在注册表中查找,如果没有使用 "blind" 数据例程,如果有则查找代理并通过它操作对象。这将注册表查找限制为只有少数实际会得到结果的查询,并消除了大量那些几乎是徒劳的,只是为了意识到该对象根本没有视觉表示。
简而言之,总结一下相对于之前设计的改进:
注册表现在要小得多,因为必须存储对象本身的指针和所有关联的 UIs 的向量我现在减少到 8 个字节
Proxy
- 指向对象的指针和任意数量的关联 UIs 的计数器
通知是自动的,只需要通知代理,它会自动通知所有引用它的UI
以前赋予 UI 的功能现在移至代理并在所有 UI 之间共享,因此 UI 本身更轻并且更容易实现,事实上,我已经从必须为每个对象类型专门化一个唯一的
QQuickItem
到能够使用通用 QMLItem
而无需实现和编译任何本机 类 对于 UI我以前必须手动管理的东西,实际的通知和负责它们的对象现在都是自动管理的
内存使用和 CPU 周期的开销已大大减少。与原始设计相比,之前的解决方案牺牲了 CPU 时间以换取更少的内存使用量,但新设计消除了大部分 CPU 开销并进一步减少了内存使用量,并且使实施更加容易和快速。
就像吃蛋糕一样:)