Professional C__ - Marc Gregoire [478]
bool Bear::eats(const Animal& inPrey) const
{
return inPrey.eatenBy(*this);
}
bool Bear::eatenBy(const Bear& inBear) const
{
return false;
}
bool Bear::eatenBy(const Fish& inFish) const
{
return false;
}
bool Bear::eatenBy(const Dinosaur& inDinosaur) const
{
return true;
}
bool Fish::eats(const Animal& inPrey) const
{
return inPrey.eatenBy(*this);
}
bool Fish::eatenBy(const Bear& inBear) const
{
return true;
}
bool Fish::eatenBy(const Fish& inFish) const
{
return true;
}
bool Fish::eatenBy(const Dinosaur& inDinosaur) const
{
return true;
}
bool Dinosaur::eats(const Animal& inPrey) const
{
return inPrey.eatenBy(*this);
}
bool Dinosaur::eatenBy(const Bear& inBear) const
{
return false;
}
bool Dinosaur::eatenBy(const Fish& inFish) const
{
return false;
}
bool Dinosaur::eatenBy(const Dinosaur& inDinosaur) const
{
return true;
}
Code snippet from DoubleDispatch\DoubleDispatch.cpp
Double dispatch is a concept that takes a bit of getting used to. We suggest playing with this code to adapt to the concept and its implementation.
Mix-In Classes
Chapters 3 and 8 introduce the technique of using multiple inheritance to build mix-in classes. Mix-in classes add a small piece of extra behavior to a class in an existing hierarchy. You can usually spot a mix-in class by its name ending in “-able”, such as Clickable, Drawable, Printable, or Lovable.
Designing a Mix-In Class
Mix-in classes come in several forms. Because mix-in classes are not a formal language feature, you can write them however you want without breaking any rules. Some mix-in classes indicate that a class supports a certain behavior, such as a hypothetical Drawable mix-in class. Any class that mixes in the Drawable class must implement the method draw(). The mix-in class itself contains no functionality — it just marks an object as supporting the draw() behavior. This usage is similar to Java’s notion of an interface — a list of prescribed behaviors without their implementation.
Other mix-in classes contain actual code. You might have a mix-in class called Playable that is mixed into certain types of media objects. The mix-in class could, for example, contain most of the code to communicate with the computer’s sound drivers. By mixing in the class, the media object would get that functionality for free.
When designing a mix-in class, you need to consider what behavior you are adding and whether it belongs in the object hierarchy or in a separate class. Using the previous example, if all media classes are playable, the base class should descend from Playable instead of mixing the Playable class into all of the subclasses. If only certain media classes are playable and they are scattered throughout the hierarchy, a mix-in class makes sense.
One of the cases where mix-in classes are particularly useful is when you have classes organized into a hierarchy on one axis, but they also contain similarity on another axis. For example, consider a war simulation game played on a grid. Each grid location can contain an Item with attack and defense capabilities and other characteristics. Some items, such as a Castle, are stationary. Others, such as a Knight or FloatingCastle, can move throughout the grid. When initially designing the object hierarchy, you might end up with something like Figure 28-1, which organizes the classes according to their attack and defense capabilities.
FIGURE 28-1
The hierarchy in Figure 28-1 ignores the movement functionality that certain classes contain. Building your hierarchy around movement would result in a structure similar to Figure 28-2.
FIGURE 28-2
Of course, the design of Figure 28-2 throws away all the organization of Figure 28-1. What’s a good object-oriented programmer to do?
There are two common solutions for this problem.