CSE 131 Module 4: Encapsulation
Update your eclipse workspace to get the
Update by right-clicking (control-click on Mac) the main project in the
package explorer, drag down to Team... and click Update.
You should see a lab4 package in the src directory.
There will be many red flags in DemoVectorTest because you have
not yet implemented the Vector class. That's OK.
Read the CSE131 Style Guide.
A substantial part of your grade on this and future labs in CSE131
will be based on style,
which includes clear code and documentation.
Good syle is important because
clarity helps avoid program errors and following standard conventions makes it easier for you and
others to understand and modify your code.
Expectations are described in the style guide.
This is the last lab in which you will not be penalized greatly
for style problems. Do your best on style and read the TA comments
carefully to avoid failing modules later on due to poor style.
In Part I,
you will define a Java class
that models a mathematical vector.
Part II involves writing methods that implement a point.
In Part III you will write a JUnit test file for verifying your
implementation of Point.
You are encouraged to work on Parts I and II using test-driven development.
Test-driven development means that the code you write is governed by the
tests your code has to pass. For this lab, you are provided
Thorough testing is important, because you will use your Vector and Point classes in later assignments,
as part of some new image manipulation and graphics tools, so you want to be sure that they work
DemoVectorTest JUnit test case. You will use the test
methods in that file to drive your implementation of
You will then write test cases as you
Part I. Implementing a Vector class:
A vector is a mathematical description of a direction and a magnitude.
Another way to think about a vector is a location relative to some point
of origin. If you think about them this way, you can see how vectors can
be added, as shown below.
- Open the file DemoVectorTest.java and take a look
at the JUnit tests that appear in the file.
You are to proceed with the development of your Vector class
by considering these tests one at a time. For example, the init()
test will require you to write the constructor for Vector.
You will have red flags because Vector is not yet complete, but
you can run the JUnit test nonetheless. If you expand the lab4.DemoVectorTest in the JUnit window, you can see which tests are passing or not.
Clicking on a given test shows you the detail below in the Failure Trace
After that, the arith() test would have you implement
Vector's plus and minus methods.
A green checkmark indicates success; a dark X indicates failure; a red
X indicates compilation problems not yet addressed.
Develop and then test each method one at a time! Read through
the instructions below before and during your develpment to guide
your writing of the Vector class.
Open the file Vector.java in the provided lab4 package, and
fill in your name and other information at the top.
A class named Vector
has been defined in the file, but so far it contains
no instance variables or methods. (Notice that the class name exactly
matches the file name, including capitalization. Java requires this.)
The instance variables of a class define what kind of information
each object of that class will hold.
If we think of a vector as a translation in two-dimensional space,
then we can represent a vector as
the change in x and the change in y.
In other words, we want each Vector object to contain two distances,
deltaX and deltaY.
To accomplish this,
declare two instance variables in the Vector class, and name them
deltaX and deltaY. Both should be of type double.
Naming conventions: By convention,
names of instance variables and methods should begin with lower case
letters, to distinguish them from class names, which begin with upper
case letters. If a variable has multi-word name, we usually
capitalize subsequent words. For example,
Encapsulation: Objects usually contain data, and it is good
design practice to make sure that this data can't be "messed with" by
other classes. Other classes should call methods on the object to
access the information. That way, each class can control what is seen
and, more importantly, how it is modified.
Make both of the instance
variables in your Vector class private by typing the keyword
private at the beginning of the declaration, before the type of
When you make the variables private, Java will make sure
that the only way that code in other classes can see or modify their
values is by calling methods of the Vector class.
Initialization: Assigning to a variable for the first time is called "initializing" the variable.
Constructurs usually have the job of initializing instance variables.
When we create Vector objects, we will probably want to supply the
deltaX and deltaY values for them. So, define a public constructor
that takes two double parameters and assigns their values to deltaX and deltaY.
Recall that a constructor always has the same name as the class.
Tip: You can type the constructor yourself if you want, but Eclipse provides
some tools for generating these kinds of constructors automatically. To create the
constructor automatically, first position your text cursor on the class name at the beginning of the file. Then open the Source menu and select "Generate constructor using fields."
Finally, select the boxes for deltaX and deltaY so that the constructor will have parameters
that are used to initialize those instance variables.
Name masking: A method or constructor may have a parameter
whose name is the same as the name of an instance variable.
For example, you might have a parameter and instance variable both with the name "deltaX."
When a name is used in a program, it refers to the "closest" declaration.
So any use of the name "deltaX" would refer to the parameter. In this case, we say that the
parameter masks the instance variable.
But inside the method or constructor, we may still want to use or change the value of the
Within a method, the keyword this
always refers to the object on which the method has been invoked
(i.e., "this" object). When an instance variable is masked, you can
still refer to it by preceding its name with "this." For
example, inside the method, this.deltaX = deltaX will assign
the value of the parameter deltaX to the instance variable named
deltaX inside "this" object.
Implicit targets: Normally, when you call a method on a target
object, you identify the target, and then identify the method and its
actual parameters. For example,
calls the deposit method on the object to which alice refers.
In other words, alice is the target. If you don't identify a
target, then it is assumed that the target is the same object that is
currently executing a method. For example, if we call the
deposit method on alice and inside of the deposit method
there is an expression getBalance(), then it is understood
that the method will be called on alice since that is the
object in which the deposit method is executing. In such cases, it is not
necessary (and considered bad style) to use the word "this" because it
is already understood that this object is the target.
- The toString method: It is customary to provide a method called
toString that takes no parameters and returns a String
value that is a textual description of the object. You can call this
method yourself, but Java will also call it whenever it needs to
concatenate the object onto a String. Define a toString
method for the Vector class that returns a textual description of the
vector. For example, (new Vector(4,3)).toString() might
have the value "[4 3]" as its return value. (Hint: Form this
string by concatenating various characters with the deltaX and deltaY
- Accessors: Most classes provide accessor methods that
other parts of a program can call to get information from an object. For
the Vector class, define two accessor methods named getDeltaX
and getDeltaY that take no parameters and return the values
of the instance variables deltaX and deltaY, respectively. Note: Here,
the instance variables will not be masked by matching parameter names
(in fact, there are no parameters at all), so there is no need to use
"this." You can refer to the instance variables by their names
alone. (Tip: You can type the methods yourself, or you can open the Eclipse
Source menu and select "Generate Getters and Setters." Check the boxes for "getDeltaX" and
"getDeltaY." Study the methods after creating them.)
At this point, your DemoVectorTest JUnit test should pass
its init() test.
- Accessors that compute their return value:
Sometimes accessors provide information that is not directly
stored inside the object, but is instead computed when the method is
called. For example, write a method
called magnitude that takes no parameters and returns a
double, the length of the vector. Use the pythagorean theorem to compute the length of the vector. Recall that the method Math.sqrt(x) returns the square root of x.
- Mutators and immutable objects:
Often, a class will provide mutator methods, such as
setDeltaX, that allow controlled modification of the data
stored in the corresponding instance variables. However, we will
not provide mutators for the Vector class. Instead, each of
our Vector objects will be immutable, meaning that once it is
created, its value will never change. So, whenever we want a Vector
with a different direction or magnitude, we will have to create a new object.
Since Vector objects will be immutable, the rest of these
methods will create new vectors as their return values.
They'll do this by first computing the desired deltaX and deltaY values, and
then using the Java keyword new to call the constructor you
wrote earlier. You can define local variables inside the methods
whenever it's convenient, but remember that the final result of each
method will be a new vector. Don't modify the object on which the method was called.
Note: You may want to add the word final to the
declaration of your instance variables. This indicates that the instance
variable's value should be established by the constructor and not be
changed by any other method. Adding final will prevent you from
accidentally changing the value in the methods you write for the
- Define a method called
deflectX that takes no parameters and returns a new vector that is
identical to "this" one, except that its
deltaX component has the opposite sign.
For example, if this vector is [-3 4], then
the new vector would be [3 4]. In other words,
the method creates a vector oriented in the opposite x direction.
Define a method called
deflectY that takes no parameters and returns a new vector that is
identical to "this" one, except that its
deltaY component has the opposite sign.
Define a method called
plus that takes another vector as its parameter and returns
a new vector that is the sum of this vector and the one
provided as input. Recall that to add two vectors, you add their x-coordinates
and their y-coordinates. For example, suppose you have a vector
with value [3 4] and you call the plus method on it, passing in
a vector with value [-5 2]. Then the vector returned by the
method should have the value [-2 6].
Hint: The parameter type and return
type of this method are both Vector. When you create the new
vector to be returned, you will need to supply parameter values.
To compute those parameter values, you can
use both "this" vector (the one on which the method was called) and
the vector that was passed in as a parameter.
Define a method called
minus that takes another vector as its parameter and returns
a new vector that is the difference of this vector minus
the one provided as input.
Challenge: Write minus in terms of methods you
have already defined for the Vector class. Which
computer science principle are you applying by doing that?
At this point, your DemoVectorTest JUnit
test should also pass its arith() test.
Define a method called
scale takes a double named
factor as its parameter. When you call this method on
a vector, it should return a new vector whose
direction is the same, but whose magnitude has been multiplied by the given
Recall: Scaling a vector by some factor
can be accomplished by scaling its deltaX and deltaY
components by that same factor. Be sure to return a new vector,
and don't change the one on which this method was called.
At this point, your DemoVectorTest JUnit
test should also pass its scale() test.
Define a method called
rescale takes a double named
magnitude as its parameter. When you call this method on
a vector, it should return a new vector whose
direction is the same, but whose magnitude is the one supplied as the
Hint: First call the magnitude
method to find this vector's magnitude and save it in a local variable.
Use this to compute a scale factor, and then let the scale method do the rest of the
NOTE: If the target of the rescale method has a zero magnitude, no particular direction is defined for the resulting vector. One could consider this an error condition, but for the purposes of this assignment, if the original magnitude is zero, let the resulting vector have deltaX equal to the given magnitude, and deltaY equal zero.
At this point,
make sure your code passes all the DemoVectorTest cases
this lab. Do NOT change the test code (we are watching and will know if
the revision changes on that file).
Part II: A Point Class
Note: You are encouraged to develop Point as you did
Vector, using test-driven development.
However, this time you have to write the tests yourself. So try to
do parts II and III simultaneously.
In the Package Explorer, select the lab4 package and use the File menu to create a new class named Point in that package. In the Point.java file that is created, write an implementation according to the specificaion described in the following steps. Just as for the Vector class, create a separate JUnit test case for the Point class, and write tests for the Point constructor and each method. Test-driven development is recommended. Write the tests as you go, rather than at the end.
- Instance variables: Since a point is just an (x,y) pair, declare two instance variables (of type double) to hold the x and y coordinates of the point. These variables should be private to prevent changes from outside. The constructor and all methods in this class should be public.
- Constructor: Write a constructor that takes x and y as parameters, and initializes the new Point object with the values that are passed in.
- Accessors: Create getX and getY methods that return the coordinate values.
- toString: Write a toString method that returns a String representation of the point. For example, if x is 3 and y is -2, you might return the String "(3.0, -2.0)".
- Adding a Vector to a Point: It doesn't make sense to add two points together, but it does make sense to add a vector to a point. The result is a new point that differs from the old point by the deltaX and deltaY of the vector. For example, if we have the point (3,-2) and we add the vector [4 1], we will get the point (7,-1). Write a plus method of the Point class that takes a Vector as a paramter and creates a new point that results from adding this point to the given vector.
- Subtracting Points:When you subtract one point from
another, you get a Vector, as shown in the figure below. As an
example, suppose point P is (4,2) and suppose that point Q is (1,6),
then P-Q would be the vector [3 -4].
(To see why this makes sense, consider what point you would get by adding the vector to Q.)
Write a minus method of the Point class that takes another Point as its parameter and returns the appropriate vector.
- Distance to another point: Write a method that takes another point as a parameter and returns the distance between this point and the given point.
Try to implement this by reducing this problem to calls of
methods you have already written
- Test all of your methods thorougly by writing tests in the PointTest.java file.
Part III: Testing with JUnit
Thorough testing is very important to help ensure that software works as expected.
JUnit is a feature of Eclipse that supports automated testing.
To begin testing your Point class, first create a JUnit test, as follows.
- In the Package Explorer window of Eclipse, right click on the Point class and select "New -> JUnit Test" from the popup menu.
- A dialog box will come up showing PointTest as the name of the test class. It is important that you do the following in the dialog box:
- At the top, select "New JUnit 4 test." (We are not using the older JUnit 3.8)
- At the bottom, you may see a warning that JUnit 4 is not on the build path. In that case, click on "Click here" in that message to add JUnit 4 to the build path. What this means is that the Java compiler will know to look in the JUnit package to find the relevant files to compile your tests. Another dialog box will come up to add to the build path. JUnit 4 should be selected already. Just click "OK."
- Click "finish."
- A new class called "PointTest.java" will appear in your project. This is where you will
put your test cases. Every file that you turn in should begin with a header comment that includes your name, lab section, etc. Copy the header comment from one of your other files and modify the information as appropriate.
- Just above your header (but after the line that says "package lab4"), add the following lines so that the compiler will import certain JUnit features:
import static org.junit.Assert.*;
- A test method: Now you're ready to begin writing some
tests your Point class. Model these after what you saw in
DemoVectorTest, but confine yourself (mostly) to testing
a Point's behaior.
- Things to remember about JUnit: Every test method must be preceded by the annotation "@Test" on the line before the start of the method.
Also, JUnit requires that every test method be public and have a "void" return type, meaning that it does not return a value. The method assertEquals is available for use in JUnit test cases. It takes two parameters: The first parameter should be what you "expect" the value to be, and the second parameter is an expression that is supposed to equal that expected value. The assertEquals method checks to see if the two parameter values are equal. When comparing doubles, you can provide a third parameter to specify an amount by which the first two parameters are allowed to differ.
When an assertEquals fails, the test will fail and JUnit will show you a failure trace so you can diagnose the problem. But the first thing to check is whether you have written the test correctly!
To run the JUnit test, right click on VectorTest in the Package Explorer, and select "Run as -> JUnit test." After running a JUnit test, results are shown in a JUnit tab at the left. Is the bar green? If so, congratulations! Your constructor, toString, and accessors must be working. If the bar isn't green, you'll see a failure trace. Click on the line of the failure trace that's inside your VectorTest program, and you will see where the failure occurred. Then you can start checking your Vector implementation to see what's wrong. After fixing the problem(s), rerun the test case to make sure that the bar is green. To rerun the test case, click the little green arrow in the JUnit window, or right click on the TestVector class and select "Run as -> JUnit Test."
Writing your own test methods:
Create test methods inside the PointTest class in concert
with your development of the Point class.
Create a test method that checks the correctness of each of your Point methods.
In each test case, you will create one or more Point and/or Vector
objects as necessary, call one or more methods,
and have one or more assertEquals statements to check the results (or
call a helper method that will eventually do the assertEquals.
We recommend adding one test case at a time, perhaps writing each test
as you complete each method of the Vector class.
Ideally, use a test-driven development methodology, as discussed above.
Run the test case, fix the method it is testing (if necessary), and then add another method to the Point class and a new corresponding test method to your PointTest class. Don't delete any of the previous test methods. By the time you are done, you'll have a number of methods that each test a different part of your class.
Remember that when the parameters are of type double, assertEquals can take a third parameter, which is the tolerance within which you are willing to treat the values as equal. For example, assertEquals(5.0, m, 0.025) says that the value of m is expected to be within 0.025 (+/-) of the value 5.0.
Be sure to create test cases that cover not only the "normal" cases, but also
cases that are likely to cause errors.
Your goal in PointTest is to
make the Point class fail so you can find errors and fix them.
To Get Credit
- Commit your work via SVN. This should cause all of your lab4 files
to be sent back to the SVN repository, including your new PointTest
file and your completed Vector and Point classes.
- Officially demo your work to a TA, making sure the demo passes muster.
Submitting your work (read carefully)
- Complete the cover-page.txt file
in the repository for this lab. Just open it up in your eclipse editor
and type in your responses.
- In the cover-page.txt file, be sure to indicate anybody with whom
you have collaborated on the work you are submitting.
- You must commit all of your work to your repository. It's best to do this
from the top-most level of your repository, which bears your name and student ID.
- You must demo the commited work to a TA. Make sure the TA knows that
your demo is for credit at this point.
- The TA must have your name and information
on a lab demo sheet for this lab to be graded and
Be sure your name and ID are written
clearly on the TA's demo sheet.
Last modified 22:14:02 CST 11 November 2009
by Ron K. Cytron