Hi all,
I would like to pose a design pattern to discussion that is achieved in two different ways. Imagine the situation where you have an interface "IFoo" (aka virtual) and implementations "FooA", "FooB", ... that inherit and implement "IFoo". Now you want the user (of the library you are creating, thus user of the source code) to use the "FooA", "FooB", ... but you dont want the user to have to mess with all the different "FooX"s. Instead you want to create a "wrapper" / "top" "Foo" which shall be used by the users and that "Foo" shall handle one of the underlying implementations "FooA", "FooB", ... that is chosen upon creation of "Foo".
Below two ways to achieve this. The first example uses a shared pointer to handle an instance of on one the special "FooA", "FooB", ... and implements all the same methods as "FooA", "FooB" .. since it also inherits "IFoo". But its' methods are just calling the underlying "FooX"'s methods.
The second example uses a templated "Foo" that inherits from one of the "FooA", "FooB" and thus provides intrinsically all the methods of the underlying "FooX".
Both compile using "g++ -std=c++0x -Wall example.cpp"
I would like to hear any thoughts on both ways to reach this. Thanks!
example1:
#include <iostream>
#include <memory>
using namespace std;
/**
* The interface Foo
*/
class IFoo {
public:
virtual void foo() const = 0;
};
/**
* First specific implementation
*/
class FooA : public IFoo {
public:
void foo() const {
cout << "Implementation specific foo A!!" << endl;
}
};
/**
* Second specific implementation
*/
class FooB : public IFoo {
public:
void foo() const {
cout << "Implementation specific foo B!!" << endl;
}
};
/**
* The foo class that can be FooA, FooB, ...
*/
class Foo : public IFoo {
std::shared_ptr<IFoo> impl_ptr;
Foo(shared_ptr<IFoo> impl)
: impl_ptr(impl)
{}
public:
static Foo create(int impl) {
if (impl == 1) {
shared_ptr<IFoo> temp(new FooA());
return Foo(temp);
} else {
shared_ptr<IFoo> temp(new FooB());
return Foo(temp);
}
}
void foo() const {
impl_ptr->foo();
}
};
int main(int argc, char **args) {
Foo foo1 = Foo::create(1);
foo1.foo();
Foo foo2 = Foo::create(2);
foo2.foo();
return 0;
}
example2:
#include <iostream>
#include <memory>
using namespace std;
/**
* The interface Foo
*/
class IFoo {
public:
virtual void foo() const = 0;
};
/**
* First specific implementation
*/
class FooA : public IFoo {
public:
void foo() const {
cout << "Implementation specific foo A!!" << endl;
}
};
/**
* Second specific implementation
*/
class FooB : public IFoo {
public:
void foo() const {
cout << "Implementation specific foo B!!" << endl;
}
};
enum TFOO
{ FOO_A, FOO_B };
/**
* The foo class that can be FooA, FooB, ...
*/
// DUMMY default class without content
template<TFOO T>
class Foo
{};
// REAL class with content if FooA case
template<>
class Foo<FOO_A> : public FooA
{};
// REAL class with content if FooB case
template<>
class Foo<FOO_B> : public FooB
{};
int main(int argc, char **args) {
Foo<FOO_A> foo_a = Foo<FOO_A>();
foo_a.foo();
Foo<FOO_B> foo_b = Foo<FOO_B>();
foo_b.foo();
return 0;
}