Seguo questo esempio sui pattern di decoratore per implementare oggetti con la quale la funzionalità può essere accoppiata dinamicamente. Vale a dire:
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.
Normalmente creare un'istanza di un oggetto con tutte le funzionalità sarà simile a:
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 )));
o
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 )));
Quest'ultimo gruppo è particolarmente utile se voglio usare una funzione come Z::doSmth()
, che non è definita nella classe "I".
Questo ha funzionato fino ad ora, perché tutte le funzionalità comuni sono state inserite nella classe "A" e la maggior parte delle funzionalità non comuni è stata classificata nelle classi XYZ. Inoltre (ed è per questo che ho usato il pattern decoratore) all'istanziazione selezionerò le coppie o terzine della funzionalità che voglio.
Concentrandosi sulle classi di decoratori derivate "XYZ", diciamo che ho un metodo virtuale puro in ciascuna di esse:
struct X /*and class Y and class Z*/ : public D {
virtual void doSmthElse() = 0;
};
Un metodo doSmthElse()
diverso dovrebbe essere eseguito per diverse istanze di "anXY", "anXYZ", ecc.
Qual è il modo corretto per farlo?
Una% derivata di% di conversione funzionerebbe? Sembra che questo metodo "distrugga" l'intero concetto del modello di decoratore. Sembra anche che il problema del "diamante" emergerebbe in questo modo, e mentre "l'ereditarietà virtuale" lo risolverebbe, dovrei comunque chiarire al compilatore quale delle stesse funzioni chiamate delle classi "XYZ" dovrebbero essere chiamate quando si accede da un oggetto di tipo "K". Un'istantanea sembrerebbe quindi
K * anK = new K( new A );
In alternativa, potrei derivare ciascuno di "XYZ" in questo modo:
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
};
Quindi posso nuovamente selezionare la funzionalità al momento dell'istanziamento in questo modo
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 )));
Questo è più semplice ed evita i mal di testa del metodo precedente, tuttavia, ancora una volta mi sembra di ricreare diverse nuove classi.
Non sono stato in grado di farlo su Google perché non so se questo "problema" ha un nome e forse uno schema di codifica che lo risolve. Se questo è un problema ben noto e puoi indicarmi una direzione per leggere qualcosa, o puoi pensare ad una soluzione diversa, ne sarei lieto di sapere.