gpt4 book ai didi

c++ - 应用程序级集合的适当架构

转载 作者:可可西里 更新时间:2023-11-01 18:37:37 25 4
gpt4 key购买 nike

给定应用程序范围的对象集合,以及许多需要频繁访问这些对象的不相关类,提供所述访问的最佳方式是什么?

例子:

// Object A, stored in collections, used to do useful things
class A
{
...
public:
QString property(const QString& propertyName) {return m_properties.value(propertyName);}

protected:
QHash<QString,QString> m_properties;
}

// Collection class, contains methods to:
// - Access members of collections
// - Add/Remove members from collection
class GlobalCollection
{
public:
// Accessors to collection/collection members
static A* getAs() {return aHash;}
static QHash<QString,A*> getAByKey(const QString& key) {return aHash.value(key);}
static QList<A*> getAsMatchingCriteria(const QString& property, const QString& value)
{
QHash<A*> subsetOfA;

foreach(A* pA, aHash.values())
{
if (pA->property(property) == value)
subsetOfA << pA;
}

return subsetOfA;
}

protected:
QHash<QString,A*> aHash;
}

// Example client class that uses A's to do its job
class Client
{
public:
// This is tied to a button click, and is executed during run-time at the user's whim
void doSomethingNonTrivialWithAs()
{
// Get A* list based on criteria, e.g. "color" == "green"
QList<A*> asWeCareAbout = ???;

// Draw all the "green" A's in a circle holding hands
foreach(A* pA, asWeCareAbout)
{
// Draw a graphical representation of pA
// If pA has "shape" == "square", get a list of all the non-"green" "square" A's and draw them looking on jealously from the shadows
// Else if pA has "shape" == "circle", draw the non-"green" "circles" cheering it on
}
}
}

假设:

  • 优先考虑小型、轻量级的类,因此客户端对象很多
  • 客户端对象可以位于 GlobalCollection 的“对等”内部的多个层,中间层不依赖于 A* 或 GlobalCollection
  • 这目前是作为单例实现的

其他解决方案的设计要求和问题:

  • 依赖注入(inject)看起来像是调用代码的不合理负担(考虑到分层),并且为了我的喜好而牺牲了太多的清晰度
  • 我不反对用静态类代替单例,但感觉也比单例好不了多少
  • 修改集合的代码是隔离的,所以我现在不担心
  • 该解决方案需要提高 GlobalCollection 和 A 中的线程安全性(考虑到多个客户端最终可能会在同一个 A* 上工作。)目前这是通过一个互斥锁和过度锁定来实现的,这在很大程度上是因为它是很难管理对 A 的访问。
  • 我正在尝试迭代以实现可测试性,而当前的设计使得客户端的几乎所有测试都需要先正确设置 GlobalCollection。
  • 在生产代码中,我们有多个 GlobalCollections(用于 A、B、C 等),因此欢迎使用模板解决方案。

虽然我正在重构遗留代码来执行此操作,但我主要关心的是首先设计正确的架构。这似乎是一个非常普遍的逻辑概念,但我看到的所有解决方案都未能解决将其用于生产的一些重要方面,或者存在明显的缺陷/权衡。也许我太挑剔了,但根据我的经验,在这种情况下,适合这项工作的工具没有缺点。

最佳答案

有一个干净、可维护和可测试的解决方案,但您在需求中拒绝了它:

Dependency injection looks like an unreasonable burden on calling code (given the layering,) and sacrifices too much clarity for my liking

我现在将忽略该要求。如果您真的想避免依赖注入(inject)(我不推荐),请参阅我的答案的结尾。

设计集合对象

  • 围绕实际集合创建一个包装器(正如您已经做的那样)是个好主意。它使您可以完全控制客户端与集合的交互(例如关于锁定)。
  • 不要让它静态化。以您可以实例化集合、使用它并最终删除它的方式设计它。毕竟,标准库和 Qt 的所有集合也是这样工作的。
  • 为集合对象引入接口(interface)。

集合访问机制的设计

The solution needs to promote thread-safety

这需要类似工厂的中介:创建一个提供对集合的访问的工厂。然后工厂可以决定何时退回新系列或现有系列。确保客户在完成后归还集合,以便您知道有多少客户正在使用它。

现在所有客户端都通过工厂访问集合。他们只看到接口(interface),看不到真正的实现。

获取对工厂的引用

既然我们引入了工厂,客户就不需要知道直接(静态)访问集合了。然而,他们仍然需要控制工厂本身。

通过将工厂注入(inject)到客户端的构造函数中,使工厂成为依赖项。这种设计清楚地表明客户依赖于工厂。它还使您能够在测试期间关闭工厂,例如用模拟替换它。

请注意,使用依赖注入(inject)并不意味着您需要使用 DI 框架。重要的是拥有一个干净、定义明确的 composition root .

避免DI

正如我已经说过的,不推荐这样做。DI 是强调可测试性的干净、解耦设计的基础。

如果还是想避免DI,那么修改上面的设计如下:

  • 创建一个提供对工厂的访问的单例。
  • 通过所有客户端的单例访问工厂。
  • 保持集合和工厂原样,即非静态且不知道任何单例。

补充说明

您的收藏及其用法听起来很像 repository pattern .我上面的设计建议符合这种相似性(例如,以狭窄范围的方式访问集合并“归还它们”)。我认为阅读存储库模式将帮助您正确设计。

关于c++ - 应用程序级集合的适当架构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33987788/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com