指向可配置实现的Pimpl习语



我读到Pimpl有利于二进制兼容性,接口有利于轻松切换实现。我需要将这两种技术结合起来,使我的应用程序能够通过配置文件切换底层实现。

以下是我当前设计的布局:

class Foo:提供面向客户端的API,我关心ABI的兼容性
class IFoo:接口类(所有纯虚拟方法,虚拟dtor)
class Vendor1Foo:实现IFoo,使用Vendor1的库
classVendor2Foo:实现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助手,就像Herb Sutter对C++11的广义pimpl习惯用法的理解一样:herbsutter.com/gotw/_101,它还必须包含根据配置实例化正确实现的逻辑

这里有皮条客习语、桥梁模式和工厂模式的元素。在我上面的例子中,initImpl()可以被认为是一个工厂方法。我正在寻找一种可能使用也可能不使用所有这些模式的解决方案。

我已经看过c++pimpl习语:依赖于模板参数的实现,以及SO上的大多数pimpl成语问题。标题看起来很有希望,但对我的特定用例没有帮助。

我不能使用C++11,正在使用Qt。D指针不能解决我的问题,因为它们绑定到一个单独的实现。

您正在寻找的是桥梁设计模式

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个接口的专业化。。。

我认为,你把所有这些事情都搞得过于复杂了。只需使用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
}

现在,只要你修复了接口,你就可以自由地添加新的实现,因为你的客户端只能通过接口访问,你可以自由地更改你的实现细节。

在我看来,您所问的问题与依赖注入非常相似。不久前,我正在寻找一个用于C++的DI框架,并找到了poccapsule。我最后没有用过,所以我不能复习,但要看一看。

这个解决方案实际上已经为我解决了,所以我把它放在这里作为答案:

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问题列表:

是否根据名称实例化类
在编译时使用C++模板在AbstractFactory中动态注册构造函数方法
在对象工厂中注册对象创建者
C++中的实体/组件系统,如何发现类型和构造组件
有没有一种方法可以从包含对象类名的字符串中实例化对象
静态变量未初始化
取消std::type_info::name 的结果映射

最新更新