I recently saw an OO design question on some forum and started thinking of using RTTI. However this must be bad design but I am unable to think of an alternative. Here is the simple question :
Create a C++ program for the following scenario using OO concepts -
My dog, named Buddy, lives in the backyard. He barks at night when he sees a cat or a squirrel that has come to visit. If he sees a frog, and he is hungry, he eats it. If he sees a frog and he isn't hungry, he plays with it. If he has eaten 2 frogs already, and is still hungry, he will let it go. If he sees a coyote, he crys for help. Sometime his friend Spot stops by, and they chase each other. If he sees any other animal, he simply watches it. I would expect that you would have an animal class, and a cat, dog, squirrel, coyote class that inherits from the animal class.
I started thinking of having a see() method in the dog class which takes an Animal argument and then checks the actual type of the object (frog, cat etc) and takes the required action - play, chase etc depending on the actual type. However this would require RTTI which must be bad design. Can anybody please suggest a better design which would avoid RTTI and also point out the mistake in my thinking?
There are a ridiculously large number of ways to satisfy this problem using "OO concepts," depending on what you want to emphasize.
Here's the simplest solution that I can come up with:
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...
If you need multiple kinds of "perceiving" animals, it's straightforward to make a virtual wrapper for see
(producing a form of double dispatch):
// On a parent class
virtual void see(Animal&) = 0;
// On Buddy
virtual void see(Animal& a) { a.seenBy(*this); }
The above requires that the Animal
class know something about the Buddy
class. If you don't like your methods being passive verbs and want to decouple Animal
from Buddy
, you can use the visitor pattern:
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);
}
};
The second mechanism could be used for purposes other than Buddy seeing an animal (possibly for that animal seeing Buddy). It is, however, more complicated.
Note that OO is definitely not the only way to solve this problem. Other solutions exist that may be more practical for this problem, such as storing the properties of the various animals that cause Buddy to bark, eat, play, etc. This additionally decouples the Buddy
class from the Animal
class (even the visitor pattern needs an exhaustive list of everything that Buddy can perceive).
The design specifically calls for recognizing certain entities in order to perform certain operations on them. Because there is no rhyme or reason with regard to why certain operations go with certain entities (ie: it's all arbitrary), what you're looking at is either type-based dispatch or property-based dispatch. I'd go with the latter.
Give each entity some set of properties. The dog would thus react based on those properties. Cat and Squirrel would have the property, "Dog should Bark at me." When the Dog encounters an entity with such a property, it would perform the appropriate action.
In this case, an entity is nothing more than the sum of its properties as well as the behaviors based on encountering other entities with various properties. The entity may also have some state associated with it. There would not be a specific Dog or Cat class. There would just be an entity with Cat-like properties and behaviors, and an entity with Dog-like properties and behaviors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With