2013-07-12 1 views
0

Я создал ColorBlock и ImageBlock классы подклассов из абстрактных Block класс. Block реализует размер и положение и имеет абстрактный метод draw(). ColorBlock использует атрибут color и рисует себя как квадрат цвета. ImageBlock использует атрибут image и рисует себя как квадрат с изображением внутри него.Раздражающая иерархия классов со смешанными атрибутами

Теперь я хочу сделать блоки подвижными, но я также хочу сохранить неподвижные блоки. В принципе, мне нужно было бы создать MovableColorBlock и MovableImageBlock, и они оба реализуют то же самое движущееся действие. Иерархия классов будет выглядеть так:

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

И, как вы видите, я реализация скользящего действия дважды. Другим способом реализации этого было бы что-то вроде:

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

Сейчас я реализует движущаяся только один раз, но Color и Image дважды. Есть ли простое решение, или мне нужен дубликат кода?

ответ

1

Это проблема, которую испытывает почти каждый графический/физический движок.

Я бы рекомендовал, чтобы подвижный блок был подклассом Block и имел цвет и блок изображений. В конце концов, не движущийся блок - это просто движущийся блок без скорости.

+0

Не совсем то, что я ищу, но tbh это единственный здравый ответ: P –

1

Вы можете посмотреть на Decorator Pattern

Для вашего конкретного случая, вы можете себе представить MovableBlock как декоратор блок, который может быть применен к ColorBlock и ImageBlock (который был бы ConcreteComponents к компоненту Block).

Ниже приведено описание example - посмотрите на описание ScrollableWindow, что упростит просмотр аналогий с вашим примером.

+0

Это тоже рабочее решение, но его трудно поддерживать и на самом деле не работает с иерархией классов C++. –

0

Вы можете сделать свои MovableColorBlock и MovableImageBlock классами друзей «MoveableBlock» и наследовать их соответствующие «не движущиеся» классы Block. Вы все еще будете иметь две дополнительные занятия, но friend декларации позволит им получить доступ к двигающимся реализациям в MoveableBlock

1

Другим варианта заключается в создании Movable интерфейса (чисто виртуальный класс), объявляющие методы для перемещения и получение подклассов, которые реализуют эти методы:


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

Редактировать

Да, если метод move() действительно одно и то же между подклассов, то это не то, что вы хотите.

Похоже, операция move() является ортогональной операции draw(), которая для меня предполагает агрегацию вместо подкласса. Итак, давайте сделаем небольшой поворот влево.

A Block имеет положение; возможно Позиционный параметр может быть то, что движимое или нет:

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; } 
}; 

Тогда ваш базовый класс Block принимает Position в качестве параметра:

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; 
}; 

Затем, если вы хотите переместить экземпляр в Block подкласс, вы» d сначала проверить movable() метод:

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

Нет необходимости создавать избыточную MovableXxxBlock классов, без повторения кода. Согласно этой схеме подвижность блока устанавливается во время выполнения, а не время компиляции; которые могут или не могут быть совместимы с вашими целями.

+0

На самом деле меня не удовлетворяет, интерфейсы вряд ли когда-либо являются правильным решением, и это один из тех случаев, когда это кажется действительно неправильным. Это на самом деле не создает правильное дерево иерархии, вы не можете (ну, вы можете делать что-либо в программировании, но не должны) просто реализовать всю функциональность с интерфейсами. –

+0

@MarkusMeskanen: см. Править. –

+0

Сейчас намного лучше, я попробую позже, когда я вернусь домой. Возможно, все еще не то, что я ищу, но позже я подумаю. –