Professional C__ - Marc Gregoire [46]
That said, one could make a convincing argument that a MultiHash actually is a Hashtable with some new functionality, and it should have been an is-a relationship. The point is that there is sometimes a fine line between the two relationships, and you will need to consider how the class is going to be used and whether what you are building just leverages some functionality from another class or really is that class with modified or new functionality.
The following table represents the arguments for and against taking either approach for the MultiHash class.
IS-A HAS-A
Reasons For • Fundamentally, it’s the same abstraction with different characteristics.
• It provides (almost) the same behaviors as Hashtable. • MultiHash can have whatever behaviors are useful without needing to worry about what behaviors Hashtable has.
• The implementation could change to something other than a Hashtable without changing the exposed behaviors.
Reasons Against • A hash table by definition has one value per key. To say MultiHash is a hash table is blasphemy!
MultiHash overrides both behaviors of Hashtable, a strong sign that something about the design is wrong.
• Unknown or inappropriate properties or behaviors of Hashtable could “bleed through” to MultiHash. • In a sense, MultiHash reinvents the wheel by coming up with new behaviors.
• Some additional properties and behaviors of Hashtable might have been useful.
The reasons against using an is-a relationship in this case are pretty strong. In fact, after years of experience, the authors recommend to opt for a has-a relationship over an is-a relationship if you have the choice.
Note that the Hashtable and MultiHash are used here to demonstrate the difference between the is-a and has-a relationships. In your own code, it is recommended to use one of the standard hash table classes instead of writing your own. The C++11 standard library provides an unordered_map class, which you should use instead of the Hashtable and an unordered_multimap class, which you should use instead of the MultiHash. Both of these standard classes are discussed in Chapter 12.
The Not-A Relationship
As you consider what type of relationship classes have, consider whether or not they actually have a relationship at all. Don’t let your zeal for object-oriented design turn into a lot of needless class/subclass relationships.
One pitfall occurs when things are obviously related in the real world but have no actual relationship in code. OO hierarchies need to model functional relationships, not artificial ones. Figure 3-7 shows relationships that are meaningful as ontologies or hierarchies, but are unlikely to represent a meaningful relationship in code.
FIGURE 3-7
The best way to avoid needless subclassing is to sketch out your design first. For every class and subclass, write down what properties and behaviors you’re planning on putting there. You should rethink your design if you find that a class has no particular properties or behaviors of its own, or if all of those properties and behaviors are completely overridden by its subclasses, except when working with abstract superclasses as mentioned earlier.
Hierarchies
Just as a class A can be a superclass of B, B can also be a superclass of C. Object-oriented hierarchies can model multilevel relationships like this. A zoo simulation with more animals might be designed with every animal as a subclass of a common Animal class as shown in Figure 3-8.
FIGURE 3-8
As you code each of these subclasses, you might find that a lot of them are similar. When this occurs, you should consider putting in a common parent. Realizing that Lion and Panther both move the same way and have the same diet might indicate a possible BigCat class. You could further subdivide the Animal class to include WaterAnimals, and Marsupials. A more hierarchical design that leverages this commonality is shown in Figure 3-9.