Removing Type Questions: Overview
If your head was swimming a bit with all the code moving around in class today, let’s try stepping back and showing the same idea with an easier and familiar example.
Imagine if someone tried writing the isNormalSize method in the AbsAnimal class as follows:
class AbsAnimal { |
int length; |
|
// constructor omitted |
|
boolean badStyleIsNormalSize() { |
if <THIS IS A DILLO> { |
return 2 <= this.length && this.length <= 3 |
} else if <THIS IS A BOA> { |
return 5 <= this.length && this.length <= 10 |
} |
} |
} |
(NOTE: the <THIS IS A DILLO> is not valid Java code. The valid version would have said (this instanceOf Dillo)).
We learned the first week that object-oriented programs put methods inside classes and let the language find the right code, rather than ask what type something is within the code. Thus, we learned to write code like this as:
class Dillo { |
... |
boolean isNormalSize() { |
return 2 <= this.length && this.length <= 3 |
} |
} |
|
class Boa { |
... |
boolean isNormalSize() { |
return 5 <= this.length && this.length <= 10 |
} |
} |
We moved the "answer" portion of each if test into a method in the class that we were asking about.
[What is the advantage of the second version? If someone adds a new animal later (such as Fish), the first version requires them to edit badStyleIsNormalSize. With the methods inside the classes, however, someone puts the new code inside the new Fish class, so existing classes don’t need to be modified to add new features to the program.]
At a high level, we just did the same code edit with the remElt and remParent code. However, the process was a bit more complicated for three reasons:
The if statements that we wanted to eliminate were buried inside another method, so we had to create a new method name (remParent) corresponding to the if statements. In the above example, the entire original method was the if statements, so we could reuse the method name isNormalSize.
The code had a nested if statement, so we had to do the process twice (introducing mergeToRemParent inside remParent).
The new remParent and mergeToRemParent methods needed parameters, because each new method was being called on a different object than the original method. For example, we called remParent on the left child; information about the right child used in the original computation therefore had to be passed along as a parameter.
With this discussion in hand, go back and see if the development of remParent makes more sense.
If you want a bit more practice with this process, the "final" code linked to the remParent notes implements largestElt poorly using a type questions (instanceof). Edit the code file to eliminate the instanceof).