Exercise Solutions (Unrestricted)
One must ensure that the messages we try out are truly messages that a dwelling could be expected to accept. Obviously fourWheelsOrTwo() is an unfair test. So what types of messages are as specific as "dwelling" but no more specific than that? Mine were:
I am happy that these are as appropriate for trailers (caravans) as they are for apartments or houses.
Write a few words on the units problem suggesting alternatives to the above.
We can reasonably complain that a date is a simple concept that ought to be simply measurable, but that the length and variety of human history has produced many, many ways of indicating the value of a date. However we measure it, though, one aspect will involve an arbitrary unit of measurement, be it the time a cesium atom takes to execute a few quantum thingamabobs or be it the duration of the king's sneeze.
In addition to time, however, there are several other dimensions of measurement (the study of which is known as dimensional analysis), length and mass being the two others of the three most basic. Then there are combinations like speed or acceleration. Thus when something hands over a simple, primitive number as the answer to a question, we must wonder whether or not it's in seconds, or grams or meters per second per second.
The adjective "primitive" above gives a clue as to one way of keeping the answer simple (low coupling) while handing over the extra necessary information:
The other possibilities are:
There aren't really many disadvantages. I only buy into one:
The advantages of doing the implementation inheritance as late as possible are many (only the optimization comes later):
Overloading is a fairly trivial convenience feature, albeit one that we are grateful for most of the time. It simply means that we don't have to invent unique names for every single method, even methods within a single class. There are more features than just its name that contribute to specifying a particular method or function being messaged for or called. In most languages the ordered types of the method's or function's parameters are matched (bound) with the ordered types of the method's or call's argument types.
Other things might also contribute to identification (binding). In C++, for example, the const-ness of an object helps determine whether a const member function, otherwise identical to another non-const member function, should be used.
Overloading has been around since before object-orientation.
Overriding, on the other hand, is more specifically an object-orientation feature. Overriding concerns superclass (base class) method (member function) declarations and subclass (derived class) decisions as to what to do about them. If a superclass has an available (e.g. non-private in C++), non-abstract method then a subclass can simply do nothing at all, whereupon a message that was declared in the superclass, being received by an instance of a subclass, will be answered by the superclass method. A subclass can choose to do something rather than nothing. A subclass can provide a method with the same distinguishing characteristics as the superclass method in question, whereupon the subclass method is said to override the superclass method, and upon receipt of an appropriate message it would be the subclass method that would be used to answer the message rather than the superclass method. (The distinguishing characteristics referred to a moment ago are often called the "signature". The signature of a message is matched to the signature of an available method.)
There is a more subtle, and very important, variety of overriding -- compulsory overriding. This concerns abstract methods, or pure virtual member functions as they are known in C++. If a superclass declares a message rather than a method, i.e. it declares what starts off looking like a method but there is, and there is declared to be, no code, then we often call that an abstract method. The more abstract declarations that a class makes, then the less concrete and more abstract the class. A concrete subclass (one that can be instantiated) of a superclass with abstract methods, must code up the abstract methods, i.e. there is compulsory overriding going on.
C++ does provide us with some headaches in this area because overloading and overriding interact with a much older and inconvenient concept: name hiding. Older languages, including Pascal and C, had the idea that it would be useful to allow programmers to employ another kind of name reuse -- to allow a nested scope block to use, and hide, a name that was already in use in a nesting scope block. When C++ comes along and considers that a derived class is a nested scope, the name hiding inherited from C and the overloading added by C++ interact in ways that can be surprising and confusing.
Java, rightly in my opinion, says that name hiding is more of an irritation than a convenience, and makes it illegal: names must be capable of being distinguished by more than just their scope in Java.
Well first-off it's a rather silly example, typical of textbooks, and one that I feel thoroughly ashamed of having used myself (long ago, honestly). I am hard-pressed to think of any system in which a vegetable object is going to be likely. However, that could just be my lack of imagination. If we assume that a vegetable object was indeed necessary, then an even bigger leap of the imagination is required to justify sending it a cook() message. [You should be similarly suspicious of examples concerning Camel and Mammal classes, Panda and Bear classes or Car and Boat classes.] Are we talking about embedded software here? Are we considering a microprocessor embedded in a vegetable and with a heater attached, in order that we can have self cooking vegetables? Or should we have sent the cook() message to an object embedded in a saucepan or an oven, with a vegetable as argument?
That's rather a subjective objection, however, so onto a more objective objection. It's not a good idea to instantiate superclasses (base classes) even if one could. Theoretically in many languages it would be possible to create an instance of a superclass if it had no abstract methods. There are good reasons not to do so under normal circumstances, however. Superclasses by their very nature take a long time to stabilize. Allowing another part of your code to create an object instance from an unstable class isn't a good idea. Some of the characteristics of implementation inheritance that make superclasses slow to stabilize have been discussed in the answer to question 12.5. It's best to keep superclass names out of parameter, return and variable declarations and out of new statements. The only place where a superclass name should normally be uttered is in the superclass declaration of a subclass definition.
[Use interfaces or pABCs for types (parameter, return and variable declarations), use concrete classes or factories to get objects, and keep the superclasses secret from all but their subclasses.]
The CriminalRecord class should define that its instances contain a list class instance. Why?
Don't have your objects asking to access objects whose location or existence they shouldn't know about.
That was the very short version. A longer version might talk about empowerment. One of the most frequent mistakes of even long-term, object-oriented developers is making their objects too dumb; one suspects that they are still hankering after dumb data structures. Instead of saying, "Give me your real part and your imaginary part so that I can calculate your inverse", ask a complex number object to simply give you its inverse. Instead of asking a student object to give you its record object in order that you can add an event; give the event to the student and ask it to remember it. Instead of asking a table how big it is and what kind of table it is, present the table to the booking and ask the booking if a table is suitable.