6

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

这是我目前的设计的布局方式:

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

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

我已经查看了c++ pimpl idiom : Implementation based on a template parameter以及关于 SO 的大多数 pimpl idiom questions。标题看起来很有希望,但它对我的特定用例没有帮助。

我不能使用 C++11 并且正在使用 Qt。 D-Pointers不能解决我的问题,因为它们绑定到单个实现。

4

4 回答 4

1

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

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

它可以使用 pimpl idiom 来实现。

头文件:

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

于 2012-10-15T19:07:09.380 回答
0

我认为,你把这一切都复杂化了。只需使用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
}

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

于 2012-10-15T19:13:32.530 回答
0

在我看来,您的要求与Dependency Injection非常相似。前段时间我在寻找 C++ 的 DI 框架,发现pococapsule。我最后没有使用它,所以我无法评论它,但看看。

于 2012-10-16T16:36:36.147 回答
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 问题的列表:

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

于 2013-06-20T14:19:23.887 回答