The goal of this lab is to explore options for code reuse, including inheritance. This lab also gives practice using Eclipse and SVN.
Make a directory for this lab and move into it. In this lab, you obtain the starter code using SVN, and you will also turn in your code using SVN. Do the following (in the command line) to checkout the code:
svn checkout file:///homes/tvandrun/Public/cs245/lab-repos/####
where ##### is the lab account you're using, for example "cs235a".
Now, open Eclipse. You can launch it either from the command line or through Applications->Programming->Eclipse. Since this will probably be the first like Eclipse is opened for the lab account, it will pick a default location for the workspace. You may chose to keep the default location or pick a new one.
After Eclipse loads, you will get a screen that looks something like this:
Click on the archy arrow all the way to the right. This will bring you to the work bench.
Now, start a new Java project. Select "create project from existing source" and enter or browse to the directory where you checked out the code. Click finish.
Take a moment to re-familiarize yourselves with the code. To run it, under the green-arrow button, select "run configurations". Double-clock on "Java Application", and under the "Arguments" tab, type in "easy" under "Program arguments:". This passes the string "easy" on the command line, which tells the program to load the puzzle in the file of that name.
In particular, notice how OpenEntry
and GivenEntry
contain redundant code.
Take note of what instance variables and what
implementation of methods they have in common.
Similarly take stock of the similarities between
RowColumn
and
Square
.
Our first attempt to fix this is going to use
a technique called composition.
The main idea is that we are going to take those things
that are common between RowColumn
and Square
and package them up into their
own class.
Make a new class, SRCShare
, which
will contain the shared code bewteen Square
and RowColumn
.
The class SRCShare
must contain three things:
Square
and RowColumn
.
This is only the instance variable entries
.
Square
and
RowColumn
.
That is, it should have Entry[] entries
as a paramter, it should initialize the
instance variable entries
,
and it should loop through the array, adding each
item to the ArrayList.
Square.actionPerformed()
and RowColumn.actionPerformed()
.
You can choose a name for this method, but note that
it is not an actionPerformed()
method,
because SRCShare
is not an action listener.
(What should this method return? We'll get back to that later.)
The idea is that each Square
and RowColumn
object will have and SRCShare
object as a
component.
Now, in both the class Square
and
the RowColumn
rewrite the repeated parts so that
they make use of SRCShare
Instead of an entries
ArrayList,
they should each have an instance variable of type
SRCShare
.
They should instantiate SRCShare
in
their constructors.
Moreover, the actionPerformed()
methods
should delegate some of their work to SRCShare
.
Now, what should that method in SRCShare
return?
Well, what information do the actionPerformed()
methods need?
They need to know (a) whether there is any violation,
and, if not, (b) whether there are any unknowns.
So, really the method in SRCShare
needs to return
two boolean values.
Here's a hack to accomplish this:
Make the method to return a boolean array.
The array should be of size 2, and
the 0th position can indicate whether there are any violations,
the 1th position whether there are any unknowns.
Finish these changes, make sure all classes you've written or modifies are adquately documented, and make sure the program runs as it did before. Finally, commit your changes. To do this, right-click on the sudoku project, select "Team", and select "Commit."
Now, consider what you did in this part.
You shipped the commonality between two classes into a third
class and then made the two original classed to be
composed from the third class.
This is known as composition, and
usually it is a very good way to allow two classes
to share code.
In this particular example, however, it turns out
to be fairly clunky.
It also wouldn't work very well to use it on the commonality between
GivenEntry
and OpenEntry
.
Revert your code back to the original version.
Now we are going to focus on GivenEntry
and
OpenEntry
.
The similarity they have is that they both
have a value
which they need to
return through a getter method,
and they both have a method called display()
.
Make a new class called Entry
,
and in the new-class wizard, indicate that this class is to be
abstract.
In the Entry
class,
make an instance variable String value
like in GivenEntry
and OpenEntry
,
but instead of being private
, make it
protected
.
Make a getter method value()
.
Also, add the following line:
public abstract Component display();
Notice that line looks like a method signature from an interface. In fact, it is, although we call it an abstract method in this context. An abstract class is something that is half-way between being an interface and a class. Alternately, you can think of it as a partially written class. Just as we have seen classes that implement interfaces, we will now see classes that extend abstract classes. This means that
We call this class extension or inheritance. The idea is that the classes that extend the abstract class inherit the members in the abstract class. The abstract class is called the parent class or super class; the classes that extend the abstract class are called child classes or subclasses.
The access modifier protected
means that
the member is accessible to the child class,
but not to the outside world.
(If a member of an abstract class is declared private,
then any child class of that abstract class still
inherit that member, but the member is inaccessible
to the child class.
Why would you ever want to do that? We'll see some examples
later this semester.)
Now, go to the classes GivenEntry
and OpenEntry
.
Change them so that they extend Entry
, that is
public class OpenEntry extends Entry implements ActionListener {
Also, delete the value
instance variables,
and the value()
methods,
since they are inherited.
Test to see that it all compiles
and still works.
When you're confident it works right,
now try to write a superclass for RowColumn
and Square
called
Container
.
Ask for help as you go along.
Again, make sure it works right, and document as you go along.
Commit your changes to the repository. That's all that needs to be done.