- Define the problem
- State, clearly and unambiguously in English, what the class is
supposed to do or represent. If you can't do this in English, you
have no hope of doing it in Java.
- List desired behaviors
- List the services this class of objects is expected to provide for
other objects.
This is also a good time to decide what information
the class contains. For example, a Cons class would contain a
"first" element and a "rest"; a Student class might contain a "name", a
"SSNumber", a "GradePointAverage", etc. You'll also need to specify
what types these are: in a Student class, the "name" would
presumably be a String, the "SSNumber" (complete with its hyphens) probably
also a String, and the "GradePointAverage" a double, while in a Cons
class for a list of ints, the "first" element would be an int and the
"rest" would be an IntList.
- Define the interface
- Describe these services more precisely in Java syntax. Typically,
each "service" will become a method in the class. This is a
good time to write the contract and
examples for each of the methods.
Since you also decided, above, what information the class
contains, you can now put this decision into Java syntax by
writing instance variable declarations.
- Write a class definition skeleton
- This is just the standard syntax for a class, together with
"placeholder" comments to remind yourself of where you're going to put
the various methods and instance variables:
abstract class MyClass extends HisClass
{
// instance variable(s)
// constructor(s)
// access method(s)
// I/O and conversion method(s)
// interesting method(s)
}
For each of the methods you defined in the previous step, categorize it
and put its contract and examples into the appropriate place, in
comments.
- Write the methods
-
Writing methods is the part that takes the most thought, but it's not too bad
if you've already done all of the above and you're not asking one
method to do too much work. Follow the design
recipe for Java methods.
I recommend writing method headers
and examples
for all the methods that you defined above, then picking one
method at a time for
filling in templates,
filling in bodies, and
testing.
Choose methods in a "bottom-up" fashion: start with
one that doesn't depend on any others, then one that depends on the
first, etc.
I also recommend writing, before any other methods (or at
least fairly early), a testing method:
public static void test () {
System.out.println ("Testing class-name.");
System.out.println ();
}
This doesn't do anything interesting yet (except tell you what class
you're working on), but as you write each new method, you'll add its
test cases to the body of the test method, and you can see
whether it works simply by invoking the test method.
Alternatively, you can try individual test cases (in BlueJ or DrJava)
by hand, but this is less and less convenient as the number of methods
and test cases grows.
- Test the class
- In the course of the design recipe for
methods, you've already tested each of the methods individually.
But you're not finished: it is possible that some non-obvious
interaction among the methods causes incorrect behavior.
So now you need to test the class as a whole.
You should already have lines in your static void test()
method that create a number of objects of the class, with different
properties, try various individual methods on them, and print the
results. Now start trying
different sequences or combinations of operations
on each object. If each such sequence of operations behaves as
expected, you have reason to believe the class is correct. If not, you
have a defect somewhere: you already have confidence in each method on
its own, so the defect must be an interaction or misunderstanding
between two or more methods (for example, there's a housekeeping chore
to be done, and each method thinks the other is doing it so it doesn't
get done, or both methods do it so it gets done twice).