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.
For example,
Represent a student, with identifying information and grades.
name
: each Student may have a different
name) age
or gradePointAverage
). A behavior is something a class instance can do, or something that can be done to a class instance. Typically, behaviors are carried out the same way for all instances of a given class, and this way doesn't change over time, but it may depend on the values of properties (above).
For example,
Represent a student, with identifying information and grades.
Properties: first and last names, student ID number, and GPA.
Behaviors: A user should be able to create a Student with specified name, ID, and GPA; to retrieve any of this information about an existing Student; and change the GPA.
In the case of a complicated class, there may be some parts that you don't want to tackle immediately; write down something about them anyway, and put them on a list of versions, e.g.
toString()
for the class, its properties, and its behaviors. Typically, each property becomes an instance variable, and each behavior becomes a method. This is a good time to write down other relevant information (e.g. in what units is a variable measured, what values are legal/illegal, etc.).
For example,
Represent a student, with identifying information and grades.
Properties: first and last names, student ID number, and GPA.
Behaviors: A user should be able to create a Student with specified name, ID, and GPA; to retrieve any of this information about an existing Student; and change the GPA.
Class name:Student
Instance variables:firstName
andlastName
of typeString
,id
of typeint
, andgpa
of typedouble
.
Methods: A constructor taking infirstName, lastName, id, gpa
in that order; accessor methodsgetFirstName()
,getLastName()
,getID()
,getGPA()
, andsetGPA(double newGPA)
; standard methodstoString()
andequals(Object other)
.
Before you write any Java code, figure out how
you're going to test your class. For example, if you wanted to test
the Student
class described above, you might say
Student joe = new Student("Joe", "Schmoe", 1479025, 3.2); joe.getFirstName() // should be "Joe" joe.getLastName() // should be "Schmoe" joe.getID() // should be 1479025 joe.getGPA() // should be approximately 3.2 joe.toString() // should be "Joe Schmoe (1479025), GPA 3.2" joe.setGPA(3.3) joe.getGPA() // should be approximately 3.3 now joe.toString() // should be "Joe Schmoe (1470925), GPA 3.3"
You'll probably want to do this in several small steps. For example, you might
start with an empty class definition, with its
javadoc
comment:
/** * Represents a student, with contact information and grades. * * @author Stephen Bloch * @version 1, Feb. 23, 2004 */ class Student { }
add instance variables:
/** * Represents a student, with contact information and grades. * * @author Stephen Bloch * @version 2, Feb. 23, 2004 */ class Student { /** Student's first and last names. * @since version 2, Feb. 23, 2004 */ private String firstName, lastName; /** Student's student ID number. * @since version 2, Feb. 23, 2004 */ private int id; /** Student's current GPA. * @since version 2, Feb. 23, 2004 */ private double gpa; }
Set up a testing infrastructure. This may be a
public static void test()
method in the class, or it may be
a separate class extending JUnit's TestCase
class, or even
just a list of method calls to be invoked interactively (if you're using
BlueJ, DrJava, or ProfessorJ). Each of
these approaches has its advantages and disadvantages: the first
approach allows you to test private methods, the second allows you to
take advantage of IDE support for running JUnit test cases and
showing you which ones passed and which failed, and the third is the
quickest (but needs to be re-typed every time you modify the class, to
make sure you haven't broken something that used to work). In
any case, this is where you'll put your test cases for each method, as
described in the design recipe for methods.
For each method, go through the design recipe for methods. Be sure to do this in order: first the methods that don't depend on any others, then methods that depend on those, and so on, so you can test each individual method before going on to the next. Also, if some methods aren't needed at all until, say, version 3 of the class, you can postpone writing them until after you've tested and debugged versions 1 and 2 of the whole class.
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).