用 C++ 构建一个带有对象存储库的工厂?

发布于 2024-08-12 04:03:14 字数 528 浏览 6 评论 0原文

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

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

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

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

I want to create a factory for creation of objects implementing an abstract interface, which would return a reference to the object that is kept internally, and objects are not replicated. The idea is pretty much the same as in the log4cxx/log4j Logger class design. I would also like to hide as much details from the client as possible, i.e. that viewing the exposed .h file would not reveal implementation details like private members etc.
Example:

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

I wonder whether there accepted published guidelines/sample code for such design, as I wouldn't like to reinvent the wheel, and the task is pretty common. I thought of using static Factory Method, Singleton repository inside, and smart pointer/reference to the concrete object as returned type. questions:

  • is there a sample simple code for such design? (the code of log4cxx is too complex to be used as a skeleton)
  • how do I hide the repository from the client completely, assuming he only sees the pure abstract Encryptor class defined encryptor.h?
  • would you suggest using smart reference or pointer as a return type? is there a standard implementation for smart reference?
  • any other suggestions will be appreciated

thanks a lot!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

属性 2024-08-19 04:03:14

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

Using a smart pointer as a return value is only usefull if there is any cleanup needed when the client doesn't need the reference to the object anymore (e.g. releasing some lock or other resource, or decrementing some reference count). If no such thing is necessary, I suggest returning a simple reference. This way, the client knows he doesn't have to manage the life-cycle of the object or anything like that. A standard implementation for smart references would be Boost.SmartPtr.
As for hiding the implementation, just put the interfaces you want to expose into pure abstract base classes and let the client obtain instances via factories. All he needs then are the headers with the abstract base classes, the headers with the factory declarations and the binary to link to.

乖乖公主 2024-08-19 04:03:14

为了隐藏实现细节,我建议使用 pImpl 习惯用法。

To hide the implementation details, I would suggest using the pImpl idiom.

口干舌燥 2024-08-19 04:03:14

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

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

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

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

Encryptor.h

class Encryptor {
public:
  virtual void encrypt(const Data & in, Data * out) = 0;
  virtual ~Encryptor();

  static std::auto_ptr<Encryptor> getEncryptor(const char * name);
};

Encryptor.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;
  }
}

Client.cpp

void foo()
{
  boost::scoped_ptr enc(Encryption::getEncryption("FOO"));

  Data in = ...;
  Data out = ...;

  enc->encrypt(in, &out);
}

To hide the implementation details make Encryption class pure virtual, with no data. This keeps the primary header file simple and without implementation details. If you want to use inheritance for code reuse then use a intermediate class like BaseEncryptionImpl (this would be in a private/implementation header file).

Only the source file that implements the static factory method getEncryptor has to include the Encryption implementations.

This factory method should return an std::auto_ptr as opposed to a raw pointer to be exception safe. The much maligned auto_ptr is designed for returning pointer from functions. Also it reduces the external dependencies of your header to a standard library as opposed to boost. Users of your class can use boost::smart_prt or boost::scoped_ptr as appropriate to their needs, both have auto_ptr constructors.

Initially I would keep the getEncryptor as simple as possible, probably using if else if etc. to decide which you should create. This is much simpler than implementing a registry of AbstractFactory in a singleton. And most of the time the registry is just moving the problem. How do you initialise the registry? You can use static objects defined with each EncryptionImpl class, whose constructors register and the destructor unregisters but this can cause problems if the linker decides you don't need these objects and so doesn't include them in your executable or library.

Encryptor.h

class Encryptor {
public:
  virtual void encrypt(const Data & in, Data * out) = 0;
  virtual ~Encryptor();

  static std::auto_ptr<Encryptor> getEncryptor(const char * name);
};

Encryptor.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;
  }
}

Client.cpp

void foo()
{
  boost::scoped_ptr enc(Encryption::getEncryption("FOO"));

  Data in = ...;
  Data out = ...;

  enc->encrypt(in, &out);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文