One of the most important tasks in designing and implementing object-oriented programs is deciding which classes or objects are responsible for executing which behavior.
In the Talking Robot lab and programming project, you created two
programs that simulated a talking robot. The first interacted with a
clock object, while the second interacted with a speech-to-text
listener.
In this lab, you will combine these two programs, creating a Talking
Robot class that has a clock
and a random number generator
as part of its included features (as part of its
state), and that has three methods,
getTimestamp
,
sayAPhrase
, and
respondTo
, that implement much of the functionality of your
first two programs. You will also create a single main
class that will construct the key objects and run various tests.
This process of improving a program's design without changing its functionality is called refactoring. To verify that the functionality is unchanged, you should do regression testing, running the same tests that you ran on the program originally to check that the results are the same.
As a program evolves, it is not uncommon to decide that a different way of organizing the classes or methods in the program would make it easier to read, to understand, or to enhance. It is not a good idea to change the structure of existing code at the same time as adding new functionality, because if the program stops working correctly it will be difficult to know whether it is the restructured functionality or the new functionality that is the cause of the problem. It is better to rearrange or redesign the existing code first, test that it still works correctly, and only then add any new functionality. The restructuring or redesigning phase, without changing the program's behavior, is called refactoring.
When you make a change to a program, you should develop new tests to make sure that your changes do what you want them to. You should also, however, re-run tests that you have run before, and make sure that the only results that change are the ones you were expecting to change due to the program changes. Re-running old tests and checking that new results match previous results is called regression testing.
As in previous labs and projects, develop your changes incrementally as you go.
TalkingRobot
project that represents a Talking Robot.
Replace the BlueJ default contents of your class with the
K template
for normal classes, using a big copy-and-paste.
Change the name of the class to match the name you gave the file.
TalkingRobot
object have?
Thinking through the design:
There are at least two items that are good candidates for becoming part of the state (
instance variables
) in your newTalkingRobot
class: the clock and the random number generator. The clock is a good candidate both because
- the physical talking robot has a clock as part of its physical state, and
- the clock will be used by more than one method (
getTimestamp
andsayAPhrase
).Since a clock can be initialized in different ways (to return the real current time, a random time, or a time from a set of test cases), it still makes sense to create and initialize the clock in the
main
method. We can pass that clock, though, to the robot, so that it has a reference to it as part of its state.The random number generator will also be used by more than one method (
sayAPhrase
andrespondTo
). In addition, it is important to only have one random number generator, whenever possible, so we would definitely not want to create a random number generator as a local variable in a method every time that method is called.
Therefore, define a clock and a random number generator as instance variables
in the TalkingRobot
class.
Initialize the random number generator to a
new object in the TalkingRobot
constructor, but initialize
the clock
from a parameter passed to the constructor as a parameter.
TalkingRobot
object
in your main
method, and
test that your program still compiles and runs as it did before (regression testing).
getTimestamp
method in the TalkingRobot
class
and move the appropriate logic from your main
method to
the new getTimestamp
method.
The signature or declaration for the new method is:public String getTimestamp()
main
method to call the new
getTimestamp
method and print its value, rather than
generate a timestamp itself.
Do regression testing (re-running old tests and comparing the
results to previous results) to make sure that your refactoring has
not changed the functionality of the program. If your actual results
do not match your expected results (and if you have verified that the
expected results should be the correct ones), analyze the results to
determine what could have caused the actual results. Then fix
your program and re-run your tests.
sayAPhrase
method in the TalkingRobot
class
and move the appropriate logic from your main
method to
the new sayAPhrase
method.
Make sure to use instance variables rather than local variables for the
clock and the random number generator.
You could choose to print the phrase in sayAPhrase
or
you could have that method return the phrase and print it in your
main
method (similar to what is being done for
getTimestamp
).
main
method to call the new
sayAPhrase
method.
Do regression testing to test your program.
main
to turn off randomizing of the clock
and enter a bunch of
test cases instead. (Add all the test cases up front, before asking the
robot to say a phrase.)
If you don't already have a loop to get the time and say a phrase
multiple times, add one now. To make sure that you don't get the same
time over and over, you should ask the clock to change the time at the
top of the loop before printing the timestamp and having the robot say a
phrase.
What are good test cases? You want to test that you set the cut-offs for your time blocks correctly, and also that you get a variety of appropriate phrases for each time block. To test your time block boundaries, test the hours before, at, and after each boundary. For example, if one of your time blocks ended at 7:59am and the next started at 8:00am, you might add the following test cases:Clock theClock= new Clock(); theClock.addTestCase(0); // minimum hour -- boundary condition theClock.addTestCase(7); // last hour in "night" time block theClock.addTestCase(8); // first hour in morning time blockTo make sure that you are not always getting the same phrase at any particular time, you may want to add each test case 3 or 4 times. Then set your loop to say a phrase at least as many times as you have test cases. (If you continue changing the time after you have exhausted your test cases, the clock will start generating random times instead.)
respondTo
method in the TalkingRobot
class
that takes a String parameter that represents the question or phrase to
which the robot is responding.
Move the appropriate logic from the main
method of your
Talking Robot programming project to
the new respondTo
method.
Make sure to use the instance variable rather than a local variable for
the random number generator.
You could choose to print the response in respondTo
or
you could have that method return the phrase and print it in your
main
method (similar to what is being done for
getTimestamp
).
main
method to call the new
respondTo
method after it gets a question or phrase from
the speech-to-text stub.
Do regression testing to test your program. (Your programming project
should already have included a loop that runs through a variety of
pre-populated test cases.)
TalkingRobot
class
and in your main
so they
now match the new structure.