Проблема полиморфизма: как проверить тип производного класса? - PullRequest
1 голос
/ 27 марта 2010

это мой первый вопрос здесь:)

Я знаю, что не должен проверять тип объекта, но вместо этого использовать dynamic_cast, но это не решит мою проблему.

У меня есть класс с именем Extension и интерфейсы с именами IExtendable и IInitializable, IUpdatable, ILoadable, IDrawable (последние четыре в основном одинаковы). Если Extension реализует интерфейс IExtendable, он может расширять себя различными объектами Extension.

Проблема в том, что я хочу разрешить расширению, которое реализует IExtendable, расширяться только с расширением, которое реализует те же интерфейсы, что и исходное расширение.

Вы, вероятно, не понимаете этот беспорядок, поэтому я пытаюсь объяснить это с помощью кода:

class IExtendable
{
public:
 IExtendable(void);

 void AddExtension(Extension*);
 void RemoveExtensionByID(unsigned int);

 vector<Extension*>* GetExtensionPtr(){return &extensions;};
private:
 vector<Extension*> extensions;
};

class IUpdatable
{
public:
 IUpdatable(void);
 ~IUpdatable(void);

 virtual void Update();
};

class Extension
{
public:
 Extension(void);
 virtual ~Extension(void);

 void Enable(){enabled=true;};
 void Disable(){enabled=false;};

 unsigned int GetIndex(){return ID;};

private:
 bool enabled;
 unsigned int ID;

 static unsigned int _indexID;
};

А теперь представьте, что я создаю расширение следующим образом:

class MyExtension : public Extension, public IExtendable, public IUpdatable, public IDrawable
{
public:
    MyExtension(void);
    virtual ~MyExtension(void);

    virtual void AddExtension(Extension*);
    virtual void Update();
    virtual void Draw();
};

И я хочу разрешить этому классу расширяться только с помощью расширений, которые реализуют те же интерфейсы (или менее). Например, я хочу, чтобы он мог использовать Extension, который реализует IUpdatable; или как IUpdatable, так и IDrawable; но например не расширение, которое реализует ILoadable. Я хочу сделать это, потому что, например, когда Update () будет вызываться для некоторого Расширения, которое реализует IExtendable и IUpdateable, оно также вызывается для этих Расширений, которые расширяют это Расширение.

Поэтому, когда я добавляю некоторое расширение к расширению, которое реализует IExtendable, а некоторые из IUpdatable, ILoadable ... Я вынужден проверить, реализует ли расширение, которое будет добавлено, эти интерфейсы. Так что в IExtendable :: AddExtension (Extension *) мне нужно сделать что-то вроде этого:

void IExtendable::AddExtension(Extension* pEx)
{
bool ok = true;
// check wheather this extension can take pEx
// do this with every interface
if ((*pEx is IUpdatable) && (*this is_not IUpdatable))
ok = false;

if (ok) this->extensions.push_back(pEx);

}

Но как? Есть идеи, что будет лучшим решением? Я не хочу использовать dynamic_cast и посмотреть, возвращает ли он значение NULL ... спасибо

Ответы [ 3 ]

6 голосов
/ 27 марта 2010

Вы должны переосмыслить свой дизайн. Если вам нужно такое поведение, возможно, введите IUpdateExtendable и IDrawExtendable.

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

0 голосов
/ 28 марта 2010

Я мог бы объединить все эти интерфейсы в один класс, чтобы получить что-то вроде этого:

class Extension
{
public:
Extension(void);
virtual ~Extension(void);

void AddExtension(Extension* pEx){extensions.push_back(pEx);};

virtual void Initialize()
{
for each (Extension* pEx in extensions) pEx->Initialize();
};

virtual void Load()
{
for each (Extension* pEx in extensions) pEx->Load();
};

virtual void Update()
{
for each (Extension* pEx in extensions) pEx->Update();
};

virtual void Draw()
{
for each (Extension* pEx in extensions) pEx->Draw();
};

private:
vector<Extension*> extensions;
};

Но это будет означать, что все методы будут вызываться для каждого добавленного расширения. Например. Будут вызваны пустые методы в каком-то добавленном расширении, которое не загружает контент и ничего не рисует.

0 голосов
/ 27 марта 2010

Просто интуиция, но в зависимости от того, что делают ваши расширения, шаблон посетителя может вам помочь. Вы также можете прочитать шаблон decorator .

...