我读过Pimpl有利于二进制兼容性,接口很适合能够轻松切换实现。我需要结合这两种技术,以便我的应用程序能够通过配置文件切换底层实现。
以下是我当前设计的布局:
类Foo:提供面向客户端的API,我关注这里的ABI兼容性
class IFoo:接口类(所有纯虚方法,虚拟dtor)
class Vendor1Foo:使用Vendor1的库实现IFoo
class Vendor2Foo:使用Vendor2的库实现IFoo
不使用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,它还必须包含根据配置实例化正确实现的逻辑
这里有pimpl习语,桥接模式和工厂模式的元素。在上面的例子中,initImpl()可以被认为是一个工厂方法。我正在寻找一种可能会或可能不会使用所有这些模式的解决方案。
我已经查看了c++ pimpl idiom : Implementation depending on a template parameter以及关于SO的大多数pimpl成语问题。标题似乎很有希望,但它对我的特定用例没有帮助。
我不能使用C ++ 11并使用Qt。 D-Pointers无法解决我的问题,因为它们只能绑定到一个实现。
答案 0 :(得分:1)
您正在寻找的是桥梁设计模式
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 :(得分:0)
我想,你把所有这些事情都复杂化了。只需使用 Foos的 factory 。
//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
}
现在,只要您修复了界面,就可以自由添加新的实现,因为您的客户只能通过界面访问,您可以自由更改实施细节。
答案 2 :(得分:0)
在我看来,与依赖注入非常相似。前段时间我正在为C ++寻找一个DI框架,并找到了pococapsule。我最后没有用它,所以我无法复习,但看看。
答案 3 :(得分: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问题:
Instantiate class from name?
Dynamically register constructor methods in an AbstractFactory at compile time using C++ templates
Register an object creator in object factory
Entity/Component Systems in C++, How do I discover types and construct components?
Is there a way to instantiate objects from a string holding their class name?
Static variable not initialized
Unmangling the result of std::type_info::name