2009-11-26 4 views
1

Я хочу создать фабрику для создания объектов, реализующих абстрактный интерфейс, которые возвратят ссылку на объект, который хранится внутри, а объекты не реплицируются. Идея в значительной степени такая же, как в дизайне класса Log4cxx/log4j Logger. Я хотел бы также, чтобы скрыть, как много деталей от клиента, как это возможно, то есть, что просмотр обнаженного файла .h не раскрывать детали реализации, как частные члены и т.д. Примера:Строительство завода с хранилищем объектов в C++?

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

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

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

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

ответ

0

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

3

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

+0

1 шаг вперед для сокрытия будет предоставлять 1 функцию и сохранить полную фабрично класса внутри страны. – stefaanv

+0

именно так, я думаю, он должен быть одноэлементным, используемым в реализации фабричного метода? – davka

+0

Не обязательно: у вас также может быть класс, создающий экземпляр фабрики с использованием идиомы 'pimpl', которые позволяют иметь несколько фабрик ... если это имеет смысл для вас. Однако в большинстве случаев эти заводы действительно должны быть уникальными и, таким образом, реализованы как «Синглтон». –

0

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

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

Этот заводский метод должен возвращать std::auto_ptr, в отличие от необработанного указателя, чтобы быть безопасным для исключений. Очень злокачественный auto_ptr предназначен для возврата указателя от функций. Также он уменьшает внешние зависимости вашего заголовка до стандартной библиотеки, а не повышает. Пользователи вашего класса могут использовать 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

спасибо за подробный ответ! У меня есть следующее беспокойство: Я ожидаю (нужно исследовать его), что я хочу иметь один конкретный объект каждого типа и поделиться им со всеми клиентами. Это связано с тем, что у некоторых объектов будет достаточно большой кеш, и для их построения потребуется чтение конфигурации. Если я клонирую объект для клиентов, мне нужно реализовать механизм кеширования. Можно было бы сохранить статическую карту объектов по имени (где-то) и либо построить все из них при запуске, либо проверить на запрос, была ли она уже построена? – davka

+0

Чтобы поделиться конкретными объектами, вероятно, вы должны вернуть share_ptr. Есть преимущества как для предварительного создания объектов, так и для их создания. Предварительно созданные могут замедлить запуск и использовать больше памяти, чем требуется, но помогут с проблемами потоковой передачи в getEcryption(). Ondemand имеет более быстрый запуск, но вызовы getEcryption могут иметь непредсказуемое время, также сложнее реализовать. Если вы не возражаете против сложной реализации и хотите повысить эффективность памяти, вы можете хранить weak_ptr в кеше. – iain

Смежные вопросы