Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
1715
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Incorporating Techniques and Frameworks

{

return false;

}

bool Dinosaur::eatenBy(const Fish& inFish) const

{

return false;

}

bool Dinosaur::eatenBy(const Dinosaur& inDinosaur) const

{

return true;

}

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 10 introduced the technique of using multiple inheritance to build mix-in classes. As you may recall, 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, 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 simply 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 simply 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 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 25-1, which organizes the classes according to their attack and defense capabilities.

747

Chapter 25

 

 

Item

 

Defender

 

 

Attacker

Castle

Barrier

Knight

Turret

FloatingCastle

 

SuperKnight

 

Figure 25-1

 

 

 

The hierarchy in Figure 25-1 ignores the movement functionality that certain classes contain. Building your hierarchy around movement would result in a structure similar to Figure 25-2.

 

 

 

Item

 

 

NonMover

 

 

Mover

Turret

Castle

Barrier

Knight

FloatingCastle

 

 

 

SuperKnight

 

Figure 25-2

Of course, the design of Figure 25-2 throws away all the organization of Figure 25-1. What’s a good object-oriented programmer to do?

There are two common solutions for this problem. Assuming that you go with the first hierarchy, organized around attackers and defenders, you need some way to work movement into the equation. One possibility is that, even though only a portion of the subclasses support movement, you could add a move() method to the Item base class. The default implementation would do nothing. Certain subclasses would override move() to actually change their location on the grid.

The other approach is to write a Movable mix-in class. The elegant hierarchy from Figure 25-1 could be preserved, but certain classes in the hierarchy would subclass Movable in addition to their parent in the diagram. Figure 25-3 shows this design.

748

Incorporating Techniques and Frameworks

Item

Movable

Defender

 

Attacker

 

Castle

Barrier

Knight

Turret

FloatingCastle

 

SuperKnight

 

Figure 25-3

Implementing a Mix-In Class

Writing a mix-in class is no different from writing a normal class. In fact, it’s usually much simpler. Using the earlier war simulation, the Movable mix-in class might look as follows:

class Movable

{

public:

virtual void move() = 0;

};

The Movable mix-in class, as defined earlier, doesn’t contain any actual functionality. However, it does two very important things. First, it provides a type for Items that can be moved. This allows you to create, for example, an array of all movable items without knowing or caring what actual subclass of Item they belong to. The Movable class also declares that all movable items must implement a method called move(). This way, you could iterate over all of the Movable objects and tell each of them to move.

Using a Mix-In Class

The code for a mix-in class is syntactically equivalent to multiple inheritance. In addition to subclassing your parent class in the main hierarchy, you also subclass the mix-in class:

class FloatingCastle : public Castle, public Movable

{

public:

virtual void move();

// Other methods and members not shown here

}

The only remaining task is to provide a definition of the move() method for FloatingCastle. Once that is done, you’ll have a class that exists in the most logical place in the hierarchy but still shares commonality with objects elsewhere in the hierarchy.

749