Pimpl成语指向可配置的实现

时间:2012-10-15 18:54:55

标签: c++ qt interface factory-pattern pimpl-idiom

我读过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无法解决我的问题,因为它们只能绑定到一个实现。

4 个答案:

答案 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