The OOP part of this exercise isn’t any more difficult than the last exercise. Two fields are changed by a mutator method and accessed (but not changed) by an accessor method.
But in the driver… oh, you’ll see.
So, there’s nothing new to see here. The
SquareRootFinder class has two
instance variables (n and iterations). The
setNumber() method allows the
user of the class to pass in a value that will be copied into the field n.
I guess I should mention that
NaN stands for “not a number”. It’s a special
value that you sometimes get in Java when you try to do something undefined
like divide zero by zero or take the square root of a negative.
getRoot() method does some complicated calculations to compute an
estimate of the square root of that number. Does it work? Yes. How does it
work? That’s the thing about programming. Sometimes you won’t know.
Go ahead and type in the driver code, then we’ll continue this thought.
I know that the square root of 4 is 2. (
2.0 when it’s a
double.) I know
that the square root of 2 is 1.414-something. I check it on my calculator on
my phone and it gives me “1.4142135624”, which fits with what the driver gives
me. I can type in a couple of other numbers and check them by hand and then
say “uh, close enough”, but how do I know?
Why does the
getRoot() method start out x with n/4? Why is iterations set
to 7 inside
setNumber()? Why not 5 or 6 or 70? Why don’t we let the user of
the class pass in a value for iterations like we do for n?
The answer to all these questions is that sometimes “the user of the class” isn’t the same person as “the creator of the class” and sometimes the user doesn’t have the training and has better things to do or would probably mess these decisions up anyway.
For most programming tasks, there is more than one person involved. It is often better to let a single person say “here is an object, you use it in this way: put in a number here and the answer will come out here.” This is a form of information hiding called “encapsulation”, and it is one of the important concepts in object-oriented programming.
In encapsulation, an object has fields and forces the user of the object to use
the methods provided instead of messing with the variables directly. In this
SquareRootFinder allows the user of the class to pass in a value for
n through the
setNumber() method but does not allow them to pass in a
value for iterations.
Okay, so how does the person who created the class know what value for iterations is “right”? I tested it. Like, a lot. Like, not just “type in a few numbers on the calculator and compare”, but like so:
In my tester program, I compare the output of the
getRoot() method with
the “real” square root as computed by Java’s built-in
Math.sqrt(). I test
every number from 0 to 2000, in increments of 0.01. For each number, I find
the absolute difference between “my” square root and the “real” square root,
and if the worst difference is more than 0.000001, then I throw an error.
Running this program over and over allowed me to test out different things.
The variable x is my initial estimate of the square root; I had initially
set x to n, but that starts to get too inaccurate as n gets bigger.
Starting x at
n/2 is better, but then very small values of n get
The best compromise I found was to start with a guess of
n/4, which gets
me close enough within seven iterations for every number in the range.
Except for values of n between 0 and 1 (where ).
I eventually gave up and just decided to give small numbers one extra
iteration to compensate for my poor initial estimate in those cases.
Often (for well-designed / well-managed software, anyway) the person who creates a class or someone else on the team will design a “test suite” for that class. For example, SQLite (a database that can be embedded into other software) is famous for being very well tested. Quoting from “How SQLite Is Tested”:
The reliability and robustness of SQLite is achieved in part by thorough and careful testing.
As of version 3.8.10, the SQLite library consists of approximately 94.2 KSLOC of C code. (KSLOC means thousands of “Source Lines Of Code” or, in other words, lines of code excluding blank lines and comments.) By comparison, the project has 971 times as much test code and test scripts - 91515.5 KSLOC.
Whenever they fix bugs or make improvements, the maintainers of SQLite run the full test suite to make sure they didn’t accidentally break anything else! This is a good idea, and the kind of modular design that OOP forces on you makes testing like this possible.
“Learn Object-Oriented Java the Hard Way” is ©2015–2016 Graham Mitchell