2010-07-04 7 views
3

Je suis en train de concevoir un framework OO et je suis confronté au problème suivant.Problème de conception orienté objet, Liskov Principe de substitution

Disons que dans le cadre que j'ai forme interface et les utilisateurs sont libres de mettre en œuvre et Prolonge (ajout de nouvelles fonctions) l'interface forme pour créer leurs propres chiffres, par exemple Carré et Cercle. Pour rendre ces nouveaux objets disponibles, les utilisateurs doivent les enregistrer dans un ShapeFactory en spécifiant le nom de la forme (chaîne) et l'objet.

En outre, le cadre fournit une interface appelée ShapeWorker qui définit la fonction suivante:

class ShapeWorker 
{ 
public: 
    void processShape(Shape& shape) = 0; 
}; 

Les utilisateurs sont libres de mettre en œuvre le ShapeWorker interface pour rendre travailleur de forme spécifique, par exemple SquareWorker et CircleWorker. Pour rendre ces nouveaux objets disponibles, les utilisateurs doivent les enregistrer dans un WorkerFactory, en spécifiant le nom de la forme (chaîne) et l'objet.

À un certain moment, le cadre, d'une chaîne représentant le nom de la forme, crée une nouvelle forme, en utilisant le ShapeFactory, et ensuite (ailleurs dans le code) crée une nouvelle ShapeWorker, en utilisant le WorkerFactory avec le même nom de forme. Le processShape est alors appelé fournissant l'instance Shape créée précédemment.

[ ... ] 
Shape* myShape = shapeFactory.create(shapeName); 
[ ... ] 
ShapeWorker* myWorker = workerFactory.create(shapeName); 
myWorker->processShape(*myShape); 
[ ... ] 

Le point est que, ce faisant, je force l'utilisateur la mise en œuvre, par exemple, le SquareWorker pour faire un bas-cast de forme-place dans la processShape fonction ainsi l'accès à l'interface de l » pleine carrés:

class SquareWorker 
{ 
public: 
    void processShape(Shape& shape) 
    { 
    Square& square = dynamic_cast< Square& >(shape); 
    // using Square interface 
    } 
}; 

Ceci est contre le principe de substitution Liskov.

Maintenant, cette approche est-elle incorrecte? Quelle serait la meilleure solution? Notez que je ne veux pas implémenter le processShape en tant que fonction membre de Shape.

J'espère que la description a été assez claire.

Merci d'avance pour votre aide.

Simo

+1

Vous devriez ajouter une balise C++ je pense. –

+0

Pouvez-vous fournir le raisonnement derrière l'instruction "Notez que je ne veux pas implémenter la processShape en tant que fonction membre de Shape"? – SCFrench

+0

L'un des problèmes ici est que le code avec des références à Shape et ShapeWorker ne sait pas s'il est sûr de passer la Shape à ShapeWorker, sans une traçabilité au point de création pour vérifier que les deux objets ont été créés en utilisant le même shapeName. – SCFrench

Répondre

5

À moins que vos formes ont une interface commune qui doit être utilisée par les travailleurs, cette approche semble tout à fait correcte pour moi. Un travailleur de forme est plus ou moins spécialisé sur une forme spécifique, a donc des connaissances sur la classe qu'il gère.Il serait plus agréable de le faire en utilisant une interface commune pour toutes les formes, mais vous ne pouvez pas mettre tout ce dont vous auriez besoin, cela finirait par être complètement encombré. Downcasting est un moyen correct pour résoudre ce problème.

L'utilisation de modèles pourrait vous aider: vous pouvez créer une classe de base pour tous les travailleurs

template <class T> 
class BaseShapeWorker : ShapeWorker 
{ 
public: 
    void processShape(Shape& shape) 
    { 
    T& specificShape = dynamic_cast< T& >(shape); 
    processShape(specificShape) 
    } 
protected: 
    virtual void processShape(T& shape) = 0; 
}; 

Ce ne serait pas besoin des implémenteurs savoir sur ce baissés et faciliter la mise en œuvre en fournissant peut-être un peu souvent fonctionnalité réutilisée.

+1

+1. NVI est un joli motif à utiliser :) –

+0

Merci jdehaan, c'est un super conseil! Cela simplifierait le travail pour les exécutants. – Simone