Exercise Solutions (Restricted)
When first encountering the concepts of inheritance (and there is certainly more than one concept to be grasped and, hence this discussion, tricky to grasp properly), one can form the incorrect impression that there will be an object of the subclass (derived class) somehow linked to an instance of the superclass (base class). That's not how it works though. Instead of two instances being linked, it is the classes that are linked, and together they produce a single object instance. (Indeed, that's two of the problems with inheritance: object encapsulation is removed rather than strengthened; and inheritance must affect the entire set of instances and is a compile time choice rather than a run-time, perhaps pluggable, choice.) So, if there is just one instance, and it's an instance that is likely to be seen as a subclass instance, one must ensure that one is clear as to what state, what instance variables, it will have available to it.
One must realize that, under the normal polymorphic kind of inheritance (public inheritance in C++), an object might receive a message that binds to a method that is an inherited method, and the said inherited method is very likely to expect and depend of the presence of superclass instance variables that must therefore have also been inherited as well.
If the inheritance mechanism required that a class using it made superclass instance variables automatically public, we would surely have stopped using inheritance altogether, many years ago. No; we must simply require that superclass instance variables are always present in subclass instances, whatever their access categories.
So what does private mean for an inherited instance variable? It means that it can only be accessed by superclass methods. Inherited variables are available only to inherited code. So some class encapsulation is at least kept, even if instance encapsulation (which in contrast, composition-delegation preserves) is lost.
[This exercise was inexplicably omitted in the first printing of the first edition. JD]
Today's languages, assuming they even have these keywords, are a bit timid as far as using them in sensible default ways. So it's important that style guides discuss the issues.
Textbooks frequently get this area wrong and don't use the keywords.
Not all languages have all the necessary mechanisms provided via keywords (C++ for example), so it's important to discuss how to get the same effect via idioms and patterns.
As discussed in the answer to question 12.8, it's very important that base classes aren't instantiated. Correctly doing two jobs (providing default implementation and transmitting type) with one relationship is difficult enough; doing three (adding instantiation) is close to impossible. So if a language has the abstract keyword (or its equivalent) to prevent a class from being instantiated, even when it has no abstract methods, then that keyword should be the default for all superclasses.
It's also very important that concrete classes don't become superclasses later in their careers. Otherwise some perfectly happy class is going to become a fragile superclass; and you will almost certainly have to carry on instantiating it, and thus break the rule about instantiating superclasses. So if a language has the final keyword (or its equivalent) to prevent a class from being subclassed, then that keyword should be the default for all concrete classes.
Type names should be as specific as they need to be, and no more specific than that; concrete class names shouldn't intrude into the space for future concrete class names.
When some piece of moderately strongly typed code declares a parameter, a return or a variable, it should be saying something like, "I need, in this resource here, something that is no less than an Asset, and no more than an Asset." To have used InvoicedCustomerThatOwesMeMoney when only Asset was required is closing the door to future flexibility. One is only programming in the present tense, and as Larry Wall I think said, one should program in the future tense.
When one is fabricating a concrete class, on the other hand, one is coming up with just one way of implementing a type. There will be other ways. If one uses up the the name Officer, one will regret that when one comes up with plain-clothes officer and realizes that the original officer was a UniformedOfficer. Concrete classes should have narrow focused names that leave room for other ways of making objects in the future.
Because I would have had an association emerging from a generally named thing that was probably an interface; and associations should not emerge from interfaces (interfaces or pABCs).
b) the purpose that the :name labels are serving in Figure 12.39 and why I would have felt uncomfortably about an example that omitted them.
This is almost same thing: associations typed with class names, especially concrete class names are not very flexible.
It wouldn’t make any difference as to whether one defined an instance variable in a subclass or its superclass; subclass instances would always store, i.e. possess, just one copy of an instance variable, whether it was defined in the subclass or in the subclass' superclass.
[Languages vary as to what would happen if one did something completely silly by defining the same instance variable in both a subclass and its superclass. JD]