【问题标题】:building a factory with object repository in C++?在 C++ 中建立一个带有对象存储库的工厂?
【发布时间】:2009-11-26 11:20:11
【问题描述】:

我想创建一个工厂来创建实现抽象接口的对象,该接口将返回对内部保存的对象的引用,并且不会复制对象。这个想法与 log4cxx/log4j Logger 类设计中的想法几乎相同。我还想尽可能多地向客户端隐藏细节,即查看暴露的 .h 文件不会显示私有成员等实现细节。 示例:

EncryptorRef = Encryptor::getEncryptor("AES");

我想知道是否有针对此类设计的已发布指南/示例代码,因为我不想重新发明轮子,而且这项任务很常见。我想过使用静态工厂方法、内部单例存储库以及对具体对象的智能指针/引用作为返回类型。问题:

  • 是否有此类设计的示例简单代码? (log4cxx的代码太复杂,无法作为骨架使用)
  • 如果客户端只看到纯抽象的Encryptor 类定义的encryptor.h,我该如何完全隐藏存储库?
  • 您是否建议使用智能引用或指针作为返回类型?是否有智能参考的标准实现?
  • 我们将不胜感激任何其他建议

非常感谢!

【问题讨论】:

    标签: c++ design-patterns oop factory-pattern


    【解决方案1】:

    仅当客户端不再需要对对象的引用时(例如释放一些锁或其他资源,或减少一些引用计数)需要进行任何清理时,使用智能指针作为返回值才有用。如果不需要这样的事情,我建议返回一个简单的参考。这样,客户就知道他不必管理对象的生命周期或类似的东西。智能引用的标准实现是 Boost.SmartPtr。 至于隐藏实现,只需将要暴露的接口放入纯抽象基类中,让客户端通过工厂获取实例即可。他所需要的只是带有抽象基类的头文件、带有工厂声明的头文件和要链接到的二进制文件。

    【讨论】:

    • 进一步的隐藏步骤是提供 1 个函数并在内部保留完整的工厂类。
    • 没错,所以我猜它应该是工厂方法实现中使用的单例?
    • 不一定:你也可以有一个使用pimpl idiom 来实例化工厂的类,它允许拥有多个工厂......如果这对你有意义的话。然而在大多数情况下,这些工厂确实是唯一的,因此实现为Singleton
    【解决方案2】:

    要隐藏实现细节,我建议使用 pImpl 成语。

    【讨论】:

      【解决方案3】:

      为了隐藏实现细节,使 Encryption 类成为纯虚拟的,没有数据。这使主头文件保持简单并且没有实现细节。如果您想使用继承来重用代码,请使用 BaseEncryptionImpl 之类的中间类(这将在私有/实现头文件中)。

      只有实现静态工厂方法getEncryptor 的源文件必须包含加密实现。

      这个工厂方法应该返回一个std::auto_ptr,而不是一个原始指针,以保证异常安全。备受诟病的auto_ptr 是为从函数返回指针而设计的。此外,它还减少了头文件对标准库的外部依赖,而不是 boost。您类的用户可以根据需要使用boost::smart_prtboost::scoped_ptr,两者都有auto_ptr 构造函数。

      最初我会尽可能简单地保持getEncryptor,可能使用if else if 等来决定你应该创建哪个。这比在单例中实现 AbstractFactory 的注册表要简单得多。大多数时候,注册表只是解决问题。你如何初始化注册表?您可以使用每个 EncryptionImpl 类定义的静态对象,其构造函数注册而析构函数注销,但如果链接器决定您不需要这些对象,因此不将它们包含在可执行文件或库中,这可能会导致问题。

      加密器.h

      class Encryptor {
      public:
        virtual void encrypt(const Data & in, Data * out) = 0;
        virtual ~Encryptor();
      
        static std::auto_ptr<Encryptor> getEncryptor(const char * name);
      };
      

      加密器.cpp

      #include "Encryptor.h"
      #include "EncryptorA.h"
      #include "EncryptorB.h"
      
      std::auto_ptr<Encryptor> Encryptor::getEncryptor(const char * name)
      {
        // EncryptorA::NAME is a std::string
        if (EncryptorA::NAME == name) {
          return std::auto_ptr<Encryptor>(new EncryptorA);
        }
        else if (EncryptorB::NAME == name) {
          return std::auto_ptr<Encryptor>(new EncryptorB);
        }
        else {
          throw EncryptionNotDefined;
        }
      }
      

      客户端.cpp

      void foo()
      {
        boost::scoped_ptr enc(Encryption::getEncryption("FOO"));
      
        Data in = ...;
        Data out = ...;
      
        enc->encrypt(in, &out);
      }
      

      【讨论】:

      • 非常感谢您的详细解答!不过,我有以下担忧:我希望(需要对其进行调查)我希望每种类型都有一个具体的对象并与所有客户共享。这是因为一些对象会有相当大的缓存,构建它们需要读取配置。如果我为客户端克隆对象,我需要实现一些缓存共享机制。一个选项可以是按名称(某处)保留对象的静态映射,或者在启动时构建所有对象,或者根据请求检查它是否已经构建?
      • 要共享具体对象,您可能应该返回一个 share_ptr。预先创建对象和按需创建对象都有好处。预创建可能会减慢启动速度并占用比所需更多的内存,但有助于解决 getEcryption() 中的线程问题。 Ondemand 启动速度更快,但对 getEcryption 的调用可能有不可预测的时间,而且实现起来也更复杂。如果您不介意复杂的实现并且想要内存效率,您可以将weak_ptr 存储在缓存中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-04
      • 2020-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多