Salut,
De manière générale, je dirais que, déjà, la classe Colleague ne devrait connaitre, au mieux, que la classe de base PrmBase et utiliser le polymorphisme des objets dérivés de PrmBase afin que sa fonction membre process puisse n'avoir qu'une seule implémentation.
Ainsi, PrmBase ne serait qu'une interface (abstraite
) proche de
1 2 3 4 5
| class PrmBase
{
public:
virtual void doSomething() = 0;
}; |
avec les classes dérivées qui se contenteraient d'implémenter la méthode en question.
Et la classe Colleague pourrait se contenter d'un
1 2 3 4 5 6 7 8 9
| class Colleague
{
public:
// la fonction n'a même pas besoin d'être virtuelle :D
void process(PrmBase const & ref)
{
ref.doSomething();
}
}; |
Si, maintenant, process doit adapter son comportement en fonction du type réel de PrmBase, tu pourrais envisager d'appliquer une chaine de responsabilité sous une forme proche de
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class ResponsibleBase
{
public:
ResponsibleBase():next_(NULL){}
virtual void handle(PrmBase const &) const =0;
//l'implémentation devrait etre dans un *.cpp ;)
virtual ~ResponsibleBas()
{
}
void add(ResponsibleBase * b)
{
if(next)
next_->add(b);
else
next_=b;
}
protected:
ResponsibleBase* next_;
};
class ResponsiblePrmDerived1 : public ResponsibleBase
{
public:
ResponsibleDerived1() //serait idéalement dans un *.cpp
{
next_=new ResponsibleDerived2;
}
virtual void handle(PrmBase const &) const;
};
class ResponsiblePrmDerived2 : public ResponsibleBase
{
public:
ResponsibleDerived2() //serait idéalement dans un *.cpp
{
next_=new ResponsibleDerived3;
}
virtual void handle(PrmBase const &) const;
};
class ResponsiblePrmDerived3 : public ResponsibleBase
{
public:
ResponsibleDerived3() //serait idéalement dans un *.cpp
{
next_=new ResponsibleDerived4;
}
virtual void handle(PrmBase const &) const;
};
class ResponsiblePrmDerived4 : public ResponsibleBase
{
public:
virtual void handle(PrmBase const &) const;
}; |
(chaque classe dérivée de ResponsibleBase créant l'instance de la classe dérivée suivante... sauf la dernière)
dont l'implémentation des méthode handle auraient le squelette suivant:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| void ResponsiblePrmDerived1::handle(PrmBase const & b) const
{
/* dynamic_cast lance une exception std::bad_cast s'il échoue
* lorsqu'il est utilisé sur une référence
*
* profitons en :D
*/
try
{
/* pour les autres, nous utiliseront les type PrmDeriv2, et autres :D
*/
PrmDeriv1 const & = dynamic_cast<PrmDeriv1 const &> (b);
/* si nous arrivons ici, b est bien du typpe PrmDeriv1 */
/* ... ce qui doit être fait */
}
catch(std::bad_cast & e)
{
/* ce n'est pas le bon type, on passe au suivant */
/* mais il n'y en a peut etre pas... */
if(!next_)
throw UnexpectedType(); // une exception "de ton cru" pouvant
// etre récupérée si ca foire ;)
next_->handle(b);
}
} |
Tu aurais alors, quelque part, mais de manière à ce la variable firstHandler soit accessible à ta classe colleague (une variable statique, dans la fonction process ou un pointeur statique en accessibilité protected dans la classe
) un code créant la chaine de responsabilité sous la forme de
ResponsiblePrmDerived1 firstHandler;
et ta fonction process deviendrait
1 2 3 4
| void Colleague::process(PrmBase const & b)
{
firstHandler.handle(b);
} |
Maintenant, ce n'est que "moyen moyen" du point de vue de la vitesse d'exécution, dans le sens où cela risque de tester tous les types dérivés de PrmBase si, d'aventure, le type réel de b est... le dernier de la liste 
Mais, de cette manière, tu limite les dépendances au grand maximum:
- Ton médiateur ne doit connaitre que
- la classe Colleague
- la déclaration de PrmBase
- la classe Colleague ne doit connaitre que
- la classe ResponsibleBase
- la déclaration de PrmBase
- chaque classe dérivée de ResponsibleBase ne doit connaitre que
- la classe dérivée de PrmBase qu'elle doit gérer
- la classe dériviée de ResponsibleBase suivante
Si, un jour, tu dois ajouter une classe dérivée de PrmBase supplémentaire, c'est "relativement" vite fait:
- tu crées la classe en question
- tu crée une nouvelle responsabilité dérivée de ResponsibleBase (en n'incluant le fichier d'en-tête de la classe dérivée de PrmBase que dans le *.cpp) (tu n'oublie pas de redéfinir la fonction handle
) - tu inclus le fichier d'en-tête de cette classe dérivée de ResponsibleBase uniquement dans le fichier qui contient l'implémentation du constructeur de la "dernière" responsabilité
- tu modifie le constructeur de la "dernière" responsabilité pour qu'il construise la nouvelle
Partager