Идиома Pimpl, указывающая на настраиваемую реализацию

Я читал, что Pimpl хорош для двоичной совместимости, а интерфейсы хороши для того, чтобы легко переключать реализацию. Мне нужно объединить оба эти метода, чтобы мое приложение могло переключать базовую реализацию через файл конфигурации.

Вот как выглядит мой текущий дизайн:

класс Foo: предоставляет клиентский интерфейс API, здесь меня интересует совместимость с ABI

class IFoo: интерфейсный класс (все чисто виртуальные методы, виртуальный dtor)

Класс Vendor1Foo: реализует IFoo, используя библиотеку Vendor1

Класс Vendor2Foo: реализует IFoo, используя библиотеку Vendor2

Не используя pimpl и строго используя интерфейсы, клиентский код может выглядеть так:

IFoo* foo = new Vendor1Foo();

Проблема в том, что мой клиентский код вообще не может знать о Vendor1 или Vendor2, и Foo — лишь один из многих классов, для которых я должен сделать это.

Общая концепция того, что я пытаюсь сделать, заключается в следующем:

class foo
{
private:
QScopedPointer<IFoo> pimpl;
void initImpl();  // Reads from QSettings and initializes pimpl
}

Есть идеи для элегантного решения этой проблемы?

Я надеюсь придумать некоторые макросы или шаблонный класс / метод, чтобы помочь стандартизировать то, как я справляюсь с этим, и свести к минимуму нарушение DRY.

Шаблонный класс мог бы служить помощником pimpl, подобно тому, как Герб Саттер использовал обобщенную идиому pimpl для C ++ 11: herbsutter.com/gotw/_101 и он также должен содержать логику для создания правильной реализации в зависимости от конфигурации

Здесь присутствуют элементы идиомы pimpl, схемы моста и фабричной схемы. В моем примере выше initImpl () можно рассматривать как фабричный метод. Я ищу решение, которое может использовать или не использовать все эти шаблоны.

Я уже смотрела идиома c ++: реализация в зависимости от параметра шаблона а также большинство вопросов о прыщах на SO. Название выглядело многообещающе, но это не помогло в моем конкретном случае использования.

Я не могу использовать C ++ 11 и использую Qt. Д-указатели не решайте мою проблему, поскольку они связаны с одной реализацией.

6

Решение

То, что вы ищете, это шаблон проектирования моста

http://en.wikipedia.org/wiki/Bridge_pattern

Это может быть реализовано с использованием языка pimpl.

Заголовочный файл:

class IFoo {
public:
virtual void doA() = 0;
virtual void dob() = 0;
};

class Foo {
public:
Foo();
~Foo();
void doA() { impl->doA(); }
void doB() { impl->doB(); }
private:
IFoo* impl;
// if needed - add clone to IFoo...
Foo(const Foo&);
Foo& operator = (const Foo&);
};

Где-нибудь еще:

   class Vendor1Foo : public IFoo { ... };
class Vendor2Foo : public IFoo { ... };

В файле .cpp:

Foo::Foo() : impl(createFooImpl()) {}

Чтобы сделать шаблон готовым к 40 классам:

template <class Interface>
Interface* createInterfaceFromConfig();

template <class Interface>
class ConcreteObject {
public:
ConcreteObject() : impl(createInterfaceFromConfig<Interface>())
Interface& interface() { return *impl; }
const Interface& interface() const { return *impl; }
private:
Interface* impl;
// if needed - add clone to IFoo...
ConcreteObject(const ConcreteObject&);
ConcreteObject& operator = (const ConcreteObject&);
};

// example
class IFoo { ... };
typedef ConcreteObject<IFoo> Foo;

// somewhere else do specialization (.cpp file)

template <>
IFoo* createInterfaceFromConfig<IFoo>() { ... }

и специализация для других 39 интерфейсов …

1

Другие решения

Я думаю, вы все усложняете. просто используйте завод из Foos.

//accessible from client code:
struct IFoo
{
virtual ~IFoo(){}
}

struct FooFactory
{
IFoo* createFoo() const;

// you may only need pimpl here to fix _factory_ _interface_.
// And you always have 1 factory
private:
FooFactroyPrivate* d;
}

//implementation:
IFoo* FooFactory::createFoo() const
{
//checking settings, creating implementation
}

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

0

То, что вы спрашиваете, мне кажется очень похожим на Внедрение зависимости. Некоторое время назад я искал DI-фреймворк для C ++ и нашел pococapsule. Я не использовал его в конце концов, поэтому я не могу его просмотреть, но посмотрите.

0

Это решение действительно сработало для меня, поэтому я помещаю его здесь в качестве ответа:

PimpleHelper.h является вспомогательным классом Pimpl для уменьшения кода котельной пластины. Использует VendorFactory для создания правильной реализации поставщика. Несколько поставщиков зарегистрируют тот факт, что они реализуют данный интерфейс; реализация только одного поставщика когда-либо была реализована для данного интерфейса.

    #include <QSettings>
#include "VendorFactory.h"#include <cxxabi.h>

// Pimpl Helper
template<typename T>
class PimplHelper
{
public:
PimplHelper()
{
m_interfaceNameImplemented = demangle(typeid(T).name());
initializeImpl();
}

T* getImpl()
{
return theImpl.data();
}

private:
QScopedPointer< T > theImpl;
QString m_interfaceNameImplemented;

void initializeImpl()
{

// Read in configuration
QSettings settings("AppSettings.ini", QSettings::IniFormat);
QString vendorToUse = settings.value("VENDOR_IMPLEMENTATION_KEY", "Vendor1").toString();

qDebug() << "Vendor to use is: " << vendorToUse << " Interface Implemented: " << m_interfaceNameImplemented;// Obtain an instance of the vendor's class that implements the T interface
theImpl.reset(
VendorFactory<T>::create(vendorToUse, m_interfaceNameImplemented)
);

if(!theImpl)
qDebug() << "PimplHelper::initializeImpl, error resolving implementation for: "<< vendorToUse << " Interface Implemented: " << m_interfaceNameImplemented;
}

const QString demangle(const char* name)
{
int status = -4;
char* res = abi::__cxa_demangle(name, NULL, NULL, &status);
const char* const demangled_name = (status==0)?res:name;
QString ret_val(demangled_name);
free(res);
return ret_val;
}
};

VendorFactory.h создает экземпляры классов, реализованных различными поставщиками.
Поставщики регистрируют свои реализации на фабрике с помощью макроса.

    #include <QtCore>

template< class T>
class VendorFactory
{
private:
typedef T* (*CreateFunc)();
typedef QMap<QString, CreateFunc> FunctionMap;

public:
static T * create(const QString& vendorName, const QString& interfaceName)
{
typename FunctionMap::iterator it = creators()->find(vendorName + interfaceName);
if (it == creators()->end())
return NULL;
return (it.value())();
}

static bool reg(const QString& vendorName, const QString& interfaceName, CreateFunc fun)
{
qDebug() << "Registering: " << vendorName + interfaceName << endl;
creators()->insert(vendorName + interfaceName, fun);
return true;
}

static FunctionMap * creators()
{
static FunctionMap* creators = new FunctionMap;
return creators;
}

virtual ~VendorFactory() {}

};/// @brief This registers a Vendor's class in the factory and adds a factory function named create_vendorImplClass()
/// and calls VendorFactory::reg() by the help of a dummy static variable to register the function.
/// @param vendorName A string representing the vendor's name
/// @param vendorImplClass The class implementing the interface given by the last parameter
/// @param interface The  interface implemented by the vendorImplClass
#define REGISTER_IN_FACTORY( vendorName, vendorImplClass, interface ) \
namespace { \
interface* create_ ## vendorImplClass() {  return new vendorImplClass; } \
static bool vendorImplClass ## _creator_registered = VendorFactory< interface >::reg( vendorName, # interface, create_ ## vendorImplClass); }

А вот как они используются:

Person.h (публичный API)

#include "IPerson.h"#include "PimplHelper.h"
// Public facing API
class Person: public IPerson
{
public:

Person()
{
impl.reset( new PimplHelper<IPerson>());
}

QString GetFirstName();
QString GetLastName();

private:
QScopedPointer< PimplHelper<IPerson> > impl;
};

Person.cpp (публичный API)

#include "Person.h"

QString Person::GetFirstName()
{   // I'd like to remove the call to getImpl() here
// and just use the overloaded -> operator, but it
// gives me a "has no member named GetFirstName()" error
return impl->getImpl()->GetFirstName();
}

QString Person::GetLastName()
{
return impl->getImpl()->GetLastName();
}

PersonImpl1.h содержит реализацию Vendor1

#include "IPerson.h"#include "VendorFactory.h"

// Private Implementation
class PersonImpl1: public IPerson
{

public:

PersonImpl1():
FirstName("Jon"), LastName("Skeet")
{}

QString GetFirstName()
{
return FirstName;
}

QString GetLastName()
{
return LastName;
}

private:
QString FirstName;
QString LastName;

};

REGISTER_IN_FACTORY("Vendor1", PersonImpl1, IPerson)

PersonImpl2.h содержит реализацию Vendor2

#include "IPerson.h"#include "VendorFactory.h"
// Private Implementation
class PersonImpl2: public IPerson
{

public:

PersonImpl2(): FirstName("Chuck"), LastName("Norris")
{}

QString GetFirstName()
{
return FirstName;
}

QString GetLastName()
{
return LastName;
}

private:
QString FirstName;
QString LastName;
};

REGISTER_IN_FACTORY("Vendor2", PersonImpl2, IPerson)

Наконец, main.cpp файл:

#include <QCoreApplication>
#include <QDebug>

#include "Person.h"
// The following needs to be included for the static/auto registration
// with the VendorFactory to occur. I'm not exactly sure why.
#include "PersonImpl1.h"#include "PersonImpl2.h"
int main(int argc, char *argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)

Person* p = new Person();

qDebug() << "The person implemented is: "<< p->GetFirstName() << " " << p->GetLastName();

qDebug() << "exiting";
}

Вот список других вопросов SO, которые помогли мне до сих пор:

Создать экземпляр класса по имени?

Динамически регистрировать методы конструктора в AbstractFactory во время компиляции с использованием шаблонов C ++

Зарегистрировать создателя объекта в фабрике объектов

Entity / Component Systems в C ++. Как мне обнаружить типы и создать компоненты?

Есть ли способ создания объектов из строки, содержащей их имя класса?

Статическая переменная не инициализирована

Отмена результата std :: type_info :: name

0
По вопросам рекламы [email protected]