Hiding multiple implementations behind one interface
I am aware of Strategy Patterns and Abstract Factory Patterns - however they do not solve my current problem:
I am creating a C ++ library that offers a very simple GUI. However, I want the user to be able to choose at compile time which graphics library to use (like Qt or FLTK) to render the GUI. However, the user only needs to know about the methods in my library.
It should be possible to compile the same code without any modifications using either the Qt backend or the FLTK backend.
I thought of something like:
class A
{
// do things that are not specific to QT or FLTK here as there are many
// methods I will need independent of the backend
}
class QT_A : public A
{
// Implement the actual creation of a window, display of a widget here using Qt
}
class FLTK_A : public A
{
// Implement the actual creation of a window, display of a widget here using FLTK
}
The problem is, I don't want the user to know about QT_A
or FLTK_A
. The user (developer) has to deal with A
. Also, I can't have both options at the same time, since I don't want my library to depend on both Qt and FLTK; only what was selected at compile time.
source to share
One option is the Pimpl idiom described in another answer.
Another option is a factory that returns a pointer to the interface class:
std::unique_ptr<A> make_A()
{
#if defined(USING_QT)
return std::unique_ptr<A>(new QT_A(...));
#elif defined(USING_FLTK)
return std::unique_ptr<A>(new FLTK_A(...));
#else
#error "No GUI library chosen"
#endif
}
source to share
The Pimpl idiom can be an alternative. This allows you to create a common interface without structure-dependent members.
class A
{
struct impl;
std::unique_ptr<impl> pimpl; // or scoped_ptr/auto_ptr on non-C++11
public:
A();
~A();
void do_sth();
};
The source file can then provide different impl implementations depending on the backend.
#ifdef QT
struct A::impl : QWidget { // Make it polymorphic, if you need
QImage img;
QString data;
};
void A::do_sth()
{
impl->make_it(); // full access to the Qt things
}
A::A()
: pimpl(new ...)
{
}
A::~A() {} // no need for delete thanks the smart pointer
#endif