Reputation: 401
I would like to know how would you address such a problem:
I have a class Foo
:
class Foo
{
public:
Foo() { }
~Foo() { }
float member1() { return _member1; }
private:
float _member1;
// other members etc...
}
A container class that, among other things, holds a container of pointers to Foo instances
class FooContainer
{
public:
FooContainer() { }
~FooContainer() { }
void addFoo(Foo* f) {_foos.push_back(f);}
private:
boost::ptr_vector<Foo> _foos;
}
My problem is this: at runtime I am required to "add" new (completely different) members to Foo, depending on the instructions from the GUI. I could address the problem by creating two "decorators" like this:
class Decorator1
{
public:
int alpha() { return _alpha; }
float beta() { return _beta; }
private:
int _alpha;
float _beta;
}
class Decorator2
{
typedef std::complex<float> cmplx;
public:
cmplx gamma() { return _gamma; }
double delta() { return _delta; }
private:
cmplx _gamma;
double _delta;
}
and then I would create two different Foo implementations:
class Foo1 : public Foo, public Decorator1
{ }
class Foo2 : public Foo, public Decorator2
{ }
and use each one according to the GUI command. However such a change would propagate through all my code and would force me to create two different versions for each class that uses Foo1
and Foo2
(e.g. I'd have to create FooContainer1
and FooContainer2
).
A less intrusive way of doing this would be to create
class Bar: public Foo, public Decorator1, public Decorator2
{ }
and use this instead of Foo
. In this case I'd call only the functions I need from Decorator1
and Decorator2
and ignore the others, but this seems to go against good OOP techniques.
Any suggestions regarding the problem ?
Upvotes: 3
Views: 4338
Reputation: 35788
How about policy based templates? Have a template class Foo that takes a class as a template parameter. Then, have two methods that call the decorator methods:
tempate <class Decor>
class Foo
{
public:
Foo() : { __d = Decor() }
~Foo() { }
float member1() { return _member1; }
Decor::method1type decoratorMember1() { return __d.getValueMethod1();}
Decor::method2type decoratorMember2() { return __d.getValueMethod2();}
private:
float _member1;
Decor __d;
// other members etc...
}
Then, in your complex decorator:
class Decor1 {
typedef std::complex<float> method1type;
typedef double method2type;
public:
method1type getValueMethod1() {return _gamma}
method2type getValueMethod2() {return _delta}
private:
method1type _gamma;
method2type _delta;
}
Same for the other. This way, your Foo
code can have anything added to it, even if it's already compiled. Just make a declarator class. And instead of instantiating Foo1
, do this:
Foo<Decor1> f;
Upvotes: 0
Reputation: 45444
the fundamental concept of a class is that it's encapsulated and hence that one cannot add members after the definition (though you can use polymorphism and create derived classes with additional members, but they cannot be called through pointer of the original class: you must cast them to derived which is dangerous), in particular not at run time.
So it seems to me you're requirement breaks the essential idea of OO programming. This suggests a simple solution: use non-member functions. They can be defined at any time, even run time (when you would also need to compile them). The overhead of the function pointer is the same as before (when you would need a pointer to a new member function).
Upvotes: 0
Reputation:
It sounds like you're looking to handle mixin-type functionality. To do that, you could use templates. This isn't run time in the sense that copies of each class will be generated, but it does save you the typing.
So for each decorator, do something like:
template<class TBase> class Decorator1 : public TBase
{
public:
void NewMethod();
}
Then you can, for example:
Foo* d = new Decorator1<Foo1>(...);
Of course, the only way to make this work at runtime is to decide which type you're going to create. However, you still end up with the type Foo
, Foo1
and Decorator1
so you can cast between them/use RTTI as you need to.
For more on this, see this article and this document
Although I've suggested it as a potential solution, I personally would be tempted to go with the polymorphism suggestion if at all possible - I think that makes for better, easier to maintain code because parts of class implementations aren't scattered all over the place using mixins. Just my two cents - if you think it works, go for it.
Upvotes: 0
Reputation: 5331
Why don't you use simple polymorphism like this?
class Foo
{
public:
Foo() { }
virtual ~Foo() { }
float member1() { return _member1; }
private:
float _member1;
// other members etc...
}
class Foo1 : public Foo
{
public:
int alpha() { return _alpha; }
float beta() { return _beta; }
private:
int _alpha;
float _beta;
}
class Foo2 : public Foo
{
typedef std::complex<float> cmplx;
public:
cmplx gamma() { return _gamma; }
double delta() { return _delta; }
private:
cmplx _gamma;
double _delta;
}
class FooContainer
{
public:
FooContainer() { }
~FooContainer() { }
void addFoo(Foo* f) {_foos.push_back(f);}
private:
boost::ptr_vector<Foo> _foos;
}
Then the client code need not change. According to the GUI command you can create Foo1 or Foo2 and add it to the single container. If necessary, you can use the dynamic_cast on Foo pointer to cast to Foo1 or Foo2. But, if you have written the client code properly, then this wouldn't be needed.
Upvotes: 8