избегая RTTI в дизайне ОО - PullRequest
       29

избегая RTTI в дизайне ОО

10 голосов
/ 15 февраля 2012

Недавно я увидел вопрос о разработке ОО на каком-то форуме и начал думать об использовании RTTI.Однако это, должно быть, плохой дизайн, но я не могу придумать альтернативы.Вот простой вопрос:

Создайте программу на C ++ для следующего сценария, используя концепции ОО -

Моя собака по имени Бадди живет на заднем дворе.Он лает ночью, когда видит кота или белку, которая пришла в гости.Если он видит лягушку и голоден, он ест ее.Если он видит лягушку и не голоден, он играет с ней.Если он уже съел 2 лягушки и все еще голоден, он отпустит его.Если он видит койота, он зовет на помощь.Иногда заходит его друг Спот, и они преследуют друг друга.Если он видит любое другое животное, он просто наблюдает за ним.Я ожидаю, что у вас будет класс животных и класс кошка, собака, белка, койот, который наследуется от класса животных.

Я начал думать о том, чтобы у собаки был метод see ().класс, который принимает аргумент Animal, а затем проверяет фактический тип объекта (лягушка, кошка и т. д.) и выполняет необходимые действия - игра, погоня и т. д. в зависимости от фактического типа.Однако это потребует RTTI, который должен быть плохим дизайном.Кто-нибудь может предложить лучший дизайн, который позволит избежать RTTI, а также указать на ошибку в моем мышлении?

Ответы [ 4 ]

13 голосов
/ 15 февраля 2012

Существует огромное количество способов решения этой проблемы с использованием «концепций ОО», в зависимости от того, что вы хотите подчеркнуть.

Вот простейшее решение, которое я могу придумать:

class Animal {
public:
    virtual void seenBy(Buddy&) = 0;
};

class Buddy {
public:
    void see(Cat&)      { /* ... */ }
    void see(Squirrel&) { /* ... */ }
    // ...
};

class Cat : public Animal {
public:
    virtual seenBy(Buddy& b) { b.see(*this); }
};

class Squirrel : public Animal {
public:
    virtual seenBy(Buddy& b) { b.see(*this); }
};

// classes for Frog, Coyote, Spot...

Если вам нужно несколько видов «воспринимающих» животных, просто создать виртуальную обертку для see (с формой двойная отправка ):

// On a parent class
virtual void see(Animal&) = 0;

// On Buddy
virtual void see(Animal& a) { a.seenBy(*this); }

Выше необходимо, чтобы класс Animal знал кое-что о классе Buddy.Если вам не нравятся ваши методы в качестве пассивных глаголов и вы хотите отделить Animal от Buddy, вы можете использовать шаблон посетителя:

class Animal {
public:
    virtual void visit(Visitor&) = 0;
};

class Cat : public Animal {
public:
    virtual void visit(Visitor& v) { v.visit(*this); }
};

class Squirrel : public Animal {
public:
    virtual void visit(Visitor& v) { v.visit(*this); }
};

// classes for Frog, Coyote, Spot...

class Visitor {
public:
    virtual void visit(Cat&) = 0;
    virtual void visit(Squirrel&) = 0;
    // ...
};

class BuddyVision : public Visitor {
public:
    virtual void visit(Cat&)      { /* ... */ }
    virtual void visit(Squirrel&) { /* ... */ }
    // ...
};

class Buddy {
public:
    void see(Animal& a) {
        BuddyVision visitor;
        a.visit(visitor);
    }
};

Второй механизм может использоваться для целей, отличных от Buddyвидя животное (возможно, это животное видит Бадди).Это, однако, сложнее.


Обратите внимание, что ОО, безусловно, не единственный способ решить эту проблему.Существуют и другие решения, которые могут быть более практичными для этой проблемы, такие как сохранение свойств различных животных, которые заставляют Бадди лаять, есть, играть и т. Д. Это дополнительно отделяет класс Buddy от класса Animal (дажешаблон посетителя нуждается в исчерпывающем списке всего, что может воспринимать Бадди).

6 голосов
/ 15 февраля 2012

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

Дайте каждой сущности некоторый набор свойств.Собака, таким образом, будет реагировать на основании этих свойств.У Кота и Белки будет свойство: «Собака должна лаять на меня».Когда Собака встречает сущность с таким свойством, она выполняет соответствующее действие.

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

3 голосов
/ 15 февраля 2012

Подсказка: используйте виртуальные функции (на целевых животных) вместо RTTI.

0 голосов
/ 15 февраля 2012

Большую часть времени вы можете заменить RTTI на обмен сообщениями.

Сортировка

Id id = object->send(WHO_ARE_YOU); 
switch(id)
{
  case ID_FROG: ...; break;
  case ID_CAT: ...; break;
}

Обмен сообщениями в принципе более гибкий, чем RTTI:

other_object->send(IS_SCARRY_OF, this); 

какэто позволяет проектировать отношения, которые неизвестны в данный момент.Скажем, завтра ваша собака увидит енота, который определен в какой-то другой DLL и еще в Паскале.

...