2013-07-12 3 views
0

J'ai créé les classes ColorBlock et ImageBlock sous-classe à partir de la classe Block abstraite. Block implémente la taille et la position et a une méthode abstraite draw(). ColorBlock implémente l'attribut color et se dessine comme un carré de couleur. ImageBlock implémente l'attribut image et se dessine comme un carré avec une image à l'intérieur.Hiérarchie de classe gênante avec attributs mixtes

Maintenant, je veux rendre les blocs mobiles, mais je veux aussi garder les blocs inamovibles. Fondamentalement, je devrais créer MovableColorBlock et MovableImageBlock, et ils implémentent tous les deux la même action de déplacement. La hiérarchie de classe serait comme ceci:

Block 
|--ColorBlock 
|----MovableColorBlock 
|--ImageBlock 
|----MovableImageBlock 

Et comme vous le voyez, j'exécution de l'action en mouvement deux fois. Une autre façon de mettre en œuvre ce serait quelque chose comme:

Block 
|--ColorBlock 
|--ImageBlock 
|--MovableBlock (abstract) 
|----MovableColorBlock 
|----MovableImageBlock 

Maintenant, je suis mise en œuvre du mouvement qu'une seule fois, mais deux fois Color et Image. Existe-t-il une solution facile ou aurai-je besoin de code en double?

Répondre

1

C'est un problème que tous les moteurs graphiques/physiques rencontrent.

Je recommanderais que le bloc Movable soit une sous-classe de Block et que les blocs Color et Image en dérivent. Après tout, un bloc non mobile est juste un bloc en mouvement sans vitesse.

+0

Pas vraiment ce que je cherche, mais c'est la seule réponse saine jusqu'à présent: P –

1

Vous pouvez regarder le Decorator Pattern

Pour votre cas particulier, vous pouvez imaginer MovableBlock comme décorateur à bloc, ce qui pourrait être appliquée à ColorBlock et ImageBlock (ce qui serait ConcreteComponents à la composante Block).

Voici une autre example - regardez la description ScrollableWindow, ce qui pourrait faciliter la comparaison avec votre exemple.

+0

Cela aussi est une solution de travail, mais difficile à maintenir et ne fonctionne pas vraiment avec la hiérarchie des classes de C++. –

0

Vous pouvez créer vos classes MovableColorBlock et MovableImageBlock amis de 'MoveableBlock' et hériter de leurs classes "non-mobiles" respectives Block. Vous aurez encore les deux classes supplémentaires, mais la déclaration friend leur permettra d'accéder à la mise en œuvre se déplaçant dans MoveableBlock

1

Une autre option est de créer une interface Movable (classe virtuelle pure) qui déclare les méthodes de déplacement, et d'en tirer des sous-classes qui mettent en œuvre ces méthodes:


Block     Movable 
    |      | 
    +-ColorBlock----------+-MovableColorBlock 
    |      | 
    +-ImageBlock----------+-MovableImageBlock 

Modifier

Oui, si la méthode move() est vraiment la même entre les sous-classes, alors ce n'est pas ce que vous voulez.

Il semble que l'opération move() soit orthogonale à l'opération draw(), ce qui me suggère une agrégation au lieu d'une sous-classe. Alors prenons un léger virage à gauche ici.

Un Block a une position; peut-être le paramètre de position pourrait être ce mobile ou non: classe

class Position 
{ 
    public: 

    Position(int x, int y) : m_x(x), m_y(y) { } 
    virtual ~Position() {} 

    int getX() const { return m_x; } 
    int getY() const { return m_y; } 

    virtual bool movable() const { return false; } 

    protected: 

    int m_x; 
    int m_y; 
}; 

class MovablePosition : public Position 
{ 
    public: 

    MovablePosition(int x, int y) : Position(x, y) {} 
    virtual ~MovablePosition() {} 

    void move(int newX, int newY) { m_x = newX; m_y = newY; } 

    virtual bool movable() const { return true; } 
}; 

Ensuite, votre base Block prend un Position comme paramètre:

class Block 
{ 
    public: 

    Block(Position *p) : m_pos(p) {} 
    virtual ~Block() {} 

    virtual void draw() = 0; 

    Position *getPos() const { return m_pos; } 

    protected: 

    Position *m_pos; 
}; 

Ensuite, si vous voulez déplacer une instance de sous-classe Block, vous d abord vérifier la méthode movable():

if (myColorBlock.getPos()->movable()) 
{ 
    MovablePosition *p = myColorBlock.getPos(); 
    p->move(newX, newY); 
    myColorBlock.draw(); 
} 

Pas besoin de créerredondante 0 classes, pas de répétition de code. Dans ce schéma, la mobilité d'un bloc est établie au moment de l'exécution et non au moment de la compilation. Cela peut ou non être compatible avec vos objectifs.

+0

Ne me satisfait pas vraiment, les interfaces ne sont presque jamais une bonne solution et c'est un de ces cas où il semble vraiment faux. Cela ne crée pas vraiment un arbre hiérarchique correct, vous ne pouvez pas (bien, vous pouvez faire n'importe quoi dans la programmation, mais vous ne devriez pas) simplement implémenter toutes les fonctionnalités avec des interfaces. –

+0

@MarkusMeskanen: voir modification. –

+0

C'est beaucoup mieux maintenant, je vais essayer plus tard aujourd'hui quand je serai à la maison. Je ne serais peut-être pas encore ce que je cherchais, mais j'y réfléchirai plus tard. –

Questions connexes