I follow this example on decorator patterns to implement objects who's functionality can be dynamically be coupled together. Namely:
class I: is the interface class, common to both "core" class and "decorator base" class
class A: is the "core functionality class"
class D: is the "decorator base" class
classes X,Y,Z: inherit from the "decorator base" class and extend functionality of the "core class" dynamically.
Normally instantiating an object that has all the functionality would look like:
I * anYZ = new Z( new Y( new A ));
I * anXY = new Y( new X( new A ));
I * anXYZ = new Z( new Y( new X( new A )));
OR
Z * anYZ = new Z( new Y( new A ));
Y * anXY = new Y( new X( new A ));
Z * anXYZ = new Z( new Y( new X( new A )));
The latter group are especially useful if I want to use a function such as Z::doSmth()
, which is not defined in class "I".
This has worked great so far, because all of the common functionality has been placed in class "A" and most the non common functionality has been categorized in classes XYZ. Additionally (and this is why I used decorator pattern) at instantiation I will select the pairs or triplets of functionality I want.
Focusing on the derived decorator classes "XYZ", say I have a pure virtual method in each of them:
struct X /*and class Y and class Z*/ : public D {
virtual void doSmthElse() = 0;
};
A different doSmthElse()
method should be executed for different instantiations of "anXY","anXYZ", etc.
What is the correct way to do this?
Would a derived class K : public X , public Y , public Z
work? It feels that this method "destroys" the whole concept of the decorator pattern. It also feels like "diamond" problem would surface this way, and while "virtual inheritance" would solve it, I would still need to clarify to the compiler which of the same-named functions of classes "XYZ" should be called when accessed from an object of type "K". An instantiation would then look like
K * anK = new K( new A );
Alternatively, I could derive each of "XYZ" like so:
struct KX : public X {
void doSmthElse(){} //one implementation of doSmthElse
};
struct LX : public X {
void doSmthElse(){} //another implementation of doSmthElse
};
struct KY : public Y {
void doSmthElse(){} //yet another implementation of doSmthElse
};
Then I can again select functionality at instantiation time like so
Z * anKxYZ = new Z( new Y( new KX( new A )));
Z * anLxYZ = new Z( new Y( new LX( new A )));
Z * anXKyZ = new Z( new KY( new X( new A )));
This is simpler and avoids headaches of the previous method, however, again it feels like I will be recreating several new classes.
I haven't been able to google about it as I do not know if this "problem" has a name and perhaps a coding pattern that solves it. If this is a well known problem and you can point me in a direction to read something, or you can think of a different solution I would gladly hear about it.