строить фабрику с хранилищем объектов в C ++? - PullRequest
1 голос
/ 26 ноября 2009

Я хочу создать фабрику для создания объектов, реализующих абстрактный интерфейс, который бы возвращал ссылку на объект, который хранится внутри, а объекты не реплицируются. Идея почти такая же, как в дизайне класса logger log4cxx / log4j. Я также хотел бы скрыть как можно больше подробностей от клиента, т. Е. Просмотр открытого файла .h не выявил бы детали реализации, такие как частные члены и т. Д. Пример:

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

Интересно, были ли приняты опубликованные руководства / примеры кода для такого дизайна, так как я не хотел бы изобретать велосипед, и эта задача довольно распространена. Я думал об использовании статического метода фабрики, репозитория Singleton внутри и умного указателя / ссылки на конкретный объект как возвращаемый тип. вопросы:

  • есть ли пример простого кода для такого дизайна? (код log4cxx слишком сложен, чтобы использовать его в качестве каркаса)
  • как мне полностью скрыть хранилище от клиента, при условии, что он видит только чистый абстрактный Encryptor определенный класс encryptor.h?
  • Вы бы предложили использовать умную ссылку или указатель в качестве типа возврата? есть ли стандартная реализация для умных ссылок?
  • Любые другие предложения будут оценены

Большое спасибо!

Ответы [ 3 ]

3 голосов
/ 26 ноября 2009

Использование интеллектуального указателя в качестве возвращаемого значения полезно только в том случае, если требуется какая-либо очистка, когда клиенту больше не нужна ссылка на объект (например, освобождение некоторой блокировки или другого ресурса или уменьшение некоторого количества ссылок). Если такая вещь не нужна, я предлагаю вернуть простую ссылку. Таким образом, клиент знает, что ему не нужно управлять жизненным циклом объекта или чем-то подобным. Стандартной реализацией для интеллектуальных ссылок будет Boost.SmartPtr. Что касается сокрытия реализации, просто поместите интерфейсы, которые вы хотите представить, в чисто абстрактные базовые классы и позвольте клиенту получать экземпляры через фабрики. Все, что ему нужно, это заголовки с абстрактными базовыми классами, заголовки с объявлениями фабрики и двоичный файл для ссылки.

0 голосов
/ 26 ноября 2009

Чтобы скрыть детали реализации, сделайте класс Encryption чисто виртуальным, без данных. Это делает основной файл заголовка простым и без подробностей реализации. Если вы хотите использовать наследование для повторного использования кода, тогда используйте промежуточный класс, такой как BaseEncryptionImpl (это будет в файле заголовка private / реализация).

Только исходный файл, который реализует статический фабричный метод getEncryptor, должен включать реализации шифрования.

Этот фабричный метод должен возвращать std::auto_ptr в отличие от необработанного указателя, чтобы быть безопасным для исключения. Клеветнический auto_ptr предназначен для возврата указателя из функций. Кроме того, он уменьшает внешние зависимости вашего заголовка до стандартной библиотеки, в отличие от boost. Пользователи вашего класса могут использовать boost::smart_prt или boost::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);
}
0 голосов
/ 26 ноября 2009

Чтобы скрыть детали реализации, я бы предложил использовать идиому pImpl.

...