Более чистая альтернатива полиморфизму - PullRequest
0 голосов
/ 17 января 2011

Построение системы графического интерфейса, и у меня есть несколько классов для различных компонентов графического интерфейса, которые являются производными от базового класса "GUIcontrol".Я хочу иметь только одну функцию для возврата любого типа компонента, но иметь возможность работать с функциями, специфичными для этого типа компонента (функции производного класса).Я заметил, что подход к полиморфизму станет проблемой, и я должен объявить все производные функции в базе, которые для этого не нужны, поскольку я никогда не буду создавать объект только из базового класса.

class GUIcontrol {
  protected:
    std::string _name;
    // these two methods (along with name()) will be used by all types
    virtual void position(/*parameters*/)
    virtual void useImage(/*parameters*/)

    // these should be only in derived types
    virtual void setHotSpot(/*parameters*/);
    virtual void setScrollButtons(/*parameters*/);
  public:
    std::string name();
  /*etc*/
}

class GUIbutton : public GUIcontrol {
  public:
    void setHotSpot(/*parameters*/);
}

class GUIscrollBar : public GUIcontrol {
  public:
    void setScrollButtons(/*parameters*/);
}

GUIcontrol* GUIsystem::getControl(std::string name);

Проблема в том, что если я хочу добавить больше функций, уникальных для GUIbutton или GUIscrollBar, или любых функций для других производных классов GUI, я также должен объявить их виртуальными в базовом классе, чтобы компилятор не жаловался на что-то вроде«setHotSpot» не является членом базового класса, который он возвращает.

Базовый класс имеет функции-члены, которые будут применяться ко всем производным классам, таким как указание объекту, где он должен быть расположен, какое изображение ему нужно использовать, как он должен называться и т. Д. Но яя не хочу наполнять базовый класс другими функциями, которые должны оставаться исключительными для определенных производных классов.

Поскольку я продолжаю добавлять больше виртуальных функций, я получу огромный объект BLOB-объектов для базового класса.Могу ли я разработать это более чистым способом?Обратите внимание, что я все еще не уверен, хочу ли я использовать static_cast / dynamic_cast для getControl (), чтобы решить эту проблему, но просто хочу знать, есть ли другие способы обойти это, чтобы очистить его.

Ответы [ 4 ]

4 голосов
/ 17 января 2011

Базовый класс должен только содержать методы для функциональности, общей для всех элементов управления.

Если вы собираетесь использовать функциональность, которая имеет смысл только для одного типа элемента управления, вы должныпроверка того, что элемент управления в любом случае имеет правильный тип, и затем может привести его к этому типу.

1 голос
/ 17 января 2011

Базовый класс исключительно общая функциональность.Если вы хотите, чтобы ваш метод вел себя по-разному для разных элементов управления, используйте dynamic_cast.Если вы хотите, чтобы он действовал одинаково для всех элементов управления, используйте виртуальный метод.

Это ваша проблема:

Мне нужно иметь только одну функцию для возврата любого типакомпонента, но иметь возможность работать с функциями, специфичными для этого типа компонента (функции производного класса).

Вам нужно относиться к ним одинаково, но по-разному.Да.Интересно, как ты собираешься заставить это работать.Вам нужно решить, хотите ли вы относиться к ним одинаково или же вы хотите относиться к ним по-другому.

0 голосов
/ 17 января 2011

Если у меня есть это право: вы хотите иметь возможность передавать объекты базового класса, но у вас есть простой способ вызывать определенные методы производного класса, где производный класс реализует эти методы?

Звучит так, будто шаблон 'mixin' может помочь:

struct Base
{
    virtual ~Base() {}
};


struct Mixin
{
    virtual ~Mixin() {}
    virtual void mixedMethod() = 0;
};

struct Concrete : Base, Mixin
{
    virtual void mixedMethod() { std::cout << "Mixing" << std:: endl; }
};

Base* create() { return new Concrete;}

bool mixIt(Base& b)
{
    Mixin* m = dynamic_cast<Mixin*>(&b);
    if (m)
        m->mixedMethod();
    return m;
}    

void test ()
{
    Base* b = create();
    assert(mixIt(*b));
    Base base;
    assert(!mixIt(base));   
}

[Да, реальный код никогда не использует struct для полиморических классов; просто держать его компактным.]

Идея в том, что доступность данного метода инкапсулирована в классе Mixin, который является чисто абстрактным базовым классом, возможно, только с одной чисто виртуальной функцией. Если вы хотите «знать», что ваш объект базового класса имеет производный тип, вы можете вызвать метод классов mixin. Вы можете заключить тест и вызов в функцию, не являющуюся членом; это позволяет вам поддерживать чистый интерфейс базовой калибровки.

0 голосов
/ 17 января 2011

Проверка типа, а затем снижение рейтинга - это неправильный способ сделать это.То, что вы должны делать, это помещать универсальные методы в ваш базовый класс, которые выполняют типы операций, которые вы хотите, и затем переопределяете их в подклассах.Например, если вы хотите, чтобы GUIControl мог рисовать сам себя, поместите метод doDraw () в базовый класс, а затем переопределите его в каждом подклассе так, как нужно.Если вместо этого вы помещаете методы getTitleBar (), getText () и т. Д. В свой подкласс, затем вызываете вызывающую программу и вызываете эти конкретные методы в зависимости от типа, ваша инкапсуляция нарушается.Если у вас есть какой-то общий код, который для рисования нужно нескольким подклассам, то вы вычленяете это либо через другой родительский класс, либо через композицию.Использование dynamic_cast или помещение определенных методов в общий подкласс, скорее всего, ухудшит ваш код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...