正确的应用架构

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

例:

// 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
  • 这目前是作为单身人士实施的
  • 其他解决方案的设计要求和问题:

  • 依赖注入看起来像调用代码时的不合理负担(给定分层),并牺牲了我的喜欢太多的清晰度
  • 我不反对静态类而不是单身,但是这并不比单身更好
  • 修改集合的代码是孤立的,所以我现在不担心这一点
  • 该解决方案需要在GlobalCollection和A中提升线程安全性(因为多个客户端最终可能会使用相同的A *)。目前,这是通过一次互斥锁和过度锁定实现的,这在很大程度上是因为它很难管理对A的访问。
  • 我试图迭代可测试性,而当前的设计几乎让客户端的每个测试都需要先正确设置GlobalCollection。
  • 在生产代码中,我们有多个GlobalCollections(适用于A,B,C等),因此欢迎使用模板解决方案。
  • 当我重构遗留代码来做到这一点时,我主要关心的是首先设计正确的架构。 这似乎是一个非常普遍的逻辑概念,但是我所看到的所有解决方案都未能解决将其用于生产的一些重要方面,或者存在明显的缺陷/折衷。 也许我太挑剔了,但以我的经验来说,正确的工具在这方面没有任何缺点。


    有一个清洁,可维护和可测试的解决方案,但您在需求中拒绝:

    依赖注入看起来像调用代码时的不合理负担(给定分层),并牺牲了我的喜欢太多的清晰度

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

    设计集合对象

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

    该解决方案需要提升线程安全性

    这为一个类似工厂的中介而尖叫:创建一个工厂,提供对集合的访问。 然后,工厂可以决定何时返回新的集合或现有的集合。 确保客户在收集完成后将其收回,以便知道有多少客户正在使用它。

    现在所有的客户都通过工厂访问集合。 他们只看到界面,从来没有真正的实现。

    获得对工厂的参考

    现在我们介绍了工厂,客户不需要知道直接(静态)访问集合。 但是,他们仍然需要掌握工厂本身。

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

    请注意,使用依赖注入并不意味着您需要使用DI框架。 重要的是它有一个清晰,明确的组合根。

    避免DI

    正如我已经说过,这不是建议。 DI是强调可测试性的干净,分离设计的基础。

    如果你仍然想避免DI,那么修改上面的设计如下:

  • 创建一个可以访问工厂的单例。
  • 通过来自所有客户的单件访问工厂。
  • 保持集合和工厂原样,即非静态的,不知道任何单身。
  • 进一步说明

    您的收藏和使用听起来很像存储库模式。 我上面的设计建议符合这种相似性(例如以狭窄范围的方式访问集合并“回馈”)。 我认为阅读有关存储库模式将帮助您获得正确的设计。

    链接地址: http://www.djcxy.com/p/89099.html

    上一篇: Proper Architecture for Application

    下一篇: docker pull lookup index.docker.io: no such host