Chapter 7 Projects
Chapter 7 Projects
Chapter 7 Projects
header
object
null
header
object
first
object
null
header
object
second
object
first
object
null
third
object
second
object
first
object
null
step 1:
step 2:
step 3:
header
object
For example, suppose you want to create a group of recipes, like those that people write on 3" x 5"
cards and put into a box in the kitchen. Each recipe is an object of the class described by the
following UML class diagram:
Recipe
-name : String
-recipe : String
-next : Recipe
+initialize(name : String, recipe : String) : void
+getName() : String
+getRecipe() : String
+getNext() : Recipe
+listNames() : void
+find(name : String) : Recipe
+findBefore(name : String) : Recipe
+removeNext() : Recipe
+insertAfter(previous : Recipe) : void
The listNames method lists the name of each object in the list, including the header. The find
method returns a reference to the object whose name is the same as the argument, but if the argument
name cannot be found, it returns a null. The findBefore method returns a reference to the object
that is before the object named in the argument, but if the argument name cannot be found, it returns a
null. The removeNext method removes from the list the object that follows the object calling the
method, and it returns a reference to the removed object. Removing an object extracts it from the list
but does not delete it. The insertAfter method inserts the object calling the method into the list
immediately after the object referenced by the argument. The following driver illustrates the use of
these methods:
/************************************************************
* RecipeDriver.java
* Dean & Dean
*
* This demonstrates creation and use of a list of recipes.
************************************************************/
import java.util.Scanner;
public class RecipeDriver
{
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
Recipe recipes = null;
// list header
Recipe before, current; // previous and current objects
Recipe recipe1;
Recipe recipe2;
Recipe recipe3;
Recipe recipe4;
String name;
recipes = new Recipe();
recipe1 = new Recipe();
// a header is required!
Modify the above driver so that it produces the same sample session by using the new
constructors instead of the combination of Javas default constructor, the initialize method,
and insertAfter method.
2. [after 7.6 ] Automobile Description *:
Write a program that asks the user how many automobiles are to be described, and for each
automobile it inputs the users selection of make and color. Then it outputs color and make. Although
you could do this easily using a simple procedural program, do it by using a main method in class
AutomobileDriver to instantiate an object called auto from a class Automobile. Make
your classes conform to the following UML class diagram:
AutomobileDriver
main(args : String[]) : void
Automobile
make : String
color : String
setMake() : void
setColor() : void
printMake() : void
printColor() : void
Let Automobile methods setMake and setColor do the prompting and inputting for make and
color, and include input verification which asks again if a selection is illegal. Let Automobile
methods printColor and printMake print color and make on one line. In
AutomobileDriver, after asking auto to call setMake and setColor, use
auto.printColor().printMake(); to chain the two printing methods. Write code so that
the program can produce the following display:
Sample session:
How many cars do you want to consider? 2
Select Buick, Chevrolet, or Pontiac (b,c,p): x
The only valid selections are 'b', 'c', or 'p'
Select Buick, Chevrolet, or Pontiac (b,c,p): p
Select blue, green, or red (b,g,r): r
red Pontiac
Select Buick, Chevrolet, or Pontiac (b,c,p): c
Select blue, green, or red (b,g,r): g
green Chevrolet
3. Carbon Cycle ***:
a) [after 7.7] Write a pair of classes for a program that models the carbon cycle in an ecosystem.
Use two generic classes. One class, Entity, defines things. The other class, Relationship,
defines interactions. Partitioning everything into these two general categories simplifies the code
in each individual category.
Relationship
+synthesize(atmosphere : Entity,
plants : Entity, time : double) : void
+eat(plants : Entity, animals : Entity,
time : double) : void
+rot(plants : Entity, animals : Entity,
bacteria : Entity, time : double) : void
+breathe(plants : Entity, animals : Entity,
bacteria : Entity, atmosphere : Entity,
time : double) : void
In the Entity class, the algorithm for the change method is:
accumulation accumulation + increment
The algorithm for the doIt method is:
mass mass + accumulation;
if (mass < 0.0)
mass 0.0
accumulation 0
The idea is to calculate all changes based on conditions at one particular time. Then, after all the
changes have been calculated, go back and implement those changes. This two-step process
avoids the inconsistency that would occur if you were to implement each change right after you
calculated it. If you implemented as you went along, some changes would be based on before
conditions and other changes would be based on after conditions.
In the Relationship class, the algorithm for the synthesize method is:
demand time * plant fixing rate * plant mass /
(1 + plant mass / plant maximum mass)
transfer demand / (1 + plant mass / atmosphere mass)
change atmosphere by -transfer
int steps;
double timeStep;
double demand;
double transfer;
//
//
//
//
10
10
10
10
11
11
11
11
12
12
bacteria=
bacteria=
bacteria=
bacteria=
bacteria=
bacteria=
bacteria=
bacteria=
bacteria=
bacteria=
103
93
92
88
87
86
85
85
84
84
b) [after 7.9] Revise the driver and Entity classes to shift all of the initialization operations to
Entity constructors.
4. [after 7.8] IP Address *:
Every computer on the Internet has a unique identifying number, called an Internet protocol (IP) address.
To contact a computer on the Internet, you send a message to the computers IP address. Here are some
typical IP addresses:
216.27.6.136
224.0.118.62
There are different formats for displaying IP addresses, but the most common format is the dotted
decimal format. The above two IP addresses use the dotted-decimal format. Its called dotted because
dots are used to split up the big IP address number into four smaller numbers. Its called decimal
because decimal numbers are used (as opposed to binary) for the four smaller numbers.
Each of the four smaller numbers is called an octet because each number represents eight bits (oct means
eight). For example, the 216 octet represents 11011000 and the 27 octet represents 00011011.
Implement an IpAddress class that stores an IP address as a dotted-decimal string and as four octet
ints.
You must implement all of the following:
Instance variables:
dottedDecimal a dotted-decimal string. Example value: "216.27.6.136"
firstOctet, secondOctet, thirdOctet, fourthOctet four int variables that store the
octets for an IP address
Constructor:
This constructor receives one parameter, a dotted-decimal string. You may assume that the parameters
value is valid (i.e., no error checking required). The constructor initializes the instance variables with
appropriate values. There are many ways to solve the problem of extracting octets from the given dotteddecimal string. We recommend that you use String methods to extract the individual octets as strings,
and then use parseInt method calls to convert the octet strings to ints.
getDottedDecimal method:
This is a standard accessor method that simply returns the dottedDecimal instance variables value.
getOctet method:
This method receives the position of one of the octets (1, 2, 3, or 4) and returns the octet thats at that
position.
Provide a driver class that tests your IpAddress class. Your driver class should contain this main
method:
public static void main(String[] args)
{
IpAddress ip = new IpAddress("216.27.6.136");
System.out.println(ip.getDottedDecimal());
System.out.println(ip.getOctet(4));
System.out.println(ip.getOctet(1));
System.out.println(ip.getOctet(3));
System.out.println(ip.getOctet(2));
} // end main
Using the above main method, your program should generate the following output.
Sample output:
216.27.6.136
136
216
6
27
add This method receives a Fraction parameter and adds the parameter fraction to the
calling object fraction.
multiply This method receives a Fraction parameter and multiplies the parameter
fraction by the calling object fraction.
print This method prints the fraction using fraction notation (1/4, 21/14, etc.)
printAsDouble This method prints the fraction as a double (0.25, 1.5, etc.)
Separate accessor methods for each instance variable in the Fraction class.
Provide a driver class, FractionDemo, that demonstrates this Fraction class. The driver class
should contain this main method:
public static void main(String[] args)
{
Scanner stdIn = new Scanner(System.in);
Fraction c, d, x;
// Fraction objects
System.out.println("Enter numerator; then denominator.");
c = new Fraction(stdIn.nextInt(), stdIn.nextInt());
c.print();
System.out.println("Enter numerator; then denominator.");
d = new Fraction(stdIn.nextInt(), stdIn.nextInt());
d.print();
x = new Fraction();
System.out.println("Sum:");
x.add(c).add(d);
x.print();
x.printAsDouble();
x = new Fraction(1, 1); // create a fraction for number 1
System.out.println("Product:");
x.multiply(c).multiply(d);
x.print();
x.printAsDouble();
System.out.println("Enter numerator; then denominator.");
x = new Fraction(stdIn.nextInt(), stdIn.nextInt());
x.printAsDouble();
} // end main
Note that this demonstration driver does not call the accessor methods. Thats OK. Accessor methods
are often implemented regardless of whether theres an immediate need for them. Thats because
they are very useful methods in general and providing them means that future code can use them
when the need arises.
Sample session:
Enter numerator; then denominator.
5
8
5/8
Enter numerator; then denominator.
4
10
4/10
Sum:
82/80
1.025
Product:
20/80
0.25
Enter numerator; then denominator.
6
0
infinity
b) Modify the code so that it can handle negative numerators and negative denominators, and
provide a helping method that performs fraction reduction.
Sample session using negative numbers and reduction:
Enter numerator; then denominator.
-5
-8
5/8
Enter numerator; then denominator.
4
-10
-2/5
Sum:
9/40
0.225
Product:
-1/4
-0.25
Enter numerator; then denominator.
0
-0
indeterminate
// 0.1 microfarad
// 1.0 millihenry
// To ground
node1.setVolts(1.0);
for (int step=0; step<steps; step++)
{
// compute changes
branchA.accumulate(timeStep);
branchB.accumulate(timeStep);
node2.accumulate(timeStep, branchA.getAmps());
node2.accumulate(timeStep, -branchB.getAmps());
// implement changes
branchA.doIt();
branchB.doIt();
node2.doIt();
// display output
if (step % outputInterval == 0)
{
System.out.println("at time = " +
(int) (1.E6 * step * timeStep) +
" microseconds: Load volts= " +
(int) (1.E3 * branchB.getAmps() * branchB.getOhms())
+ " millivolts");
}
} // end for
} // end main
} // end ElectricCircuitDriver class
The driver creates and exercises the following circuit:
node1
branchA
Source
resistance
(100 Ohms)
1-volt
step
Filter
inductance
(1.0 mH)
Capacitance
microFarad
node2
branchB
Filter
inductance
(1.0 mH)
Load
resistance
(100 Ohms)
Ground node
Define classes for nodes and branches in accordance with the following UML diagram:
Load
voltage
Node
-farads : double = 1.0E-12
-accumulation : double = 0.0
-volts : double = 0.0
+setFarads(farads : double) : void
+setVolts(volts : double) : void
+getVolts() : double
+accumulate(timeStep : double, amps : double) : void
+doIt() : void
Branch
-from : Node
-to : Node
-henries : double = 1.0E-9
-ohms : double = 0.0
-accumulation : double = 0.0
-amps : double = 0.0
+setFrom(from : Node) : void
+setTo(to : Node) : void
+setHenrys(henrys : double) : void
+setOhms(ohms : double) : void
+setAmps(amps : double) : void
+getOhms() : double
+getAmps() : double
+accumulate(timeStep : double) : void
+doIt() : void
The nodes accumulate method accumulates voltage change in one time increment, using the
current at the beginning of that time increment. Its algorithm is:
accumulation accumulation + timeStep * amps / farads
The algorithm for the Node.doIt method is:
volts volts + accumulation
accumulation 0
The branchs accumulate method computes current change in one time increment, using voltages
and current at the beginning of that time increment. If the Node parameter is null, the
corresponding volts = 0.0. Otherwise, it is the volts of that node. The voltage drop is the from
node volts minus the to node volts. Its algorithm is:
accumulation accumulation + timeStep *
((fromVolts - toVolts) - amps * ohms) / henrys;
The algorithm for the Branch.doIt method is:
amps amps + accumulation
accumulation 0
Sample session:
Enter seconds per timeStep(<1.0E-8): 1e-8
Enter number of steps(15000?): 15000
Enter steps per output(1000?): 1000
at time = 0 microseconds: Load volts= 0 millivolts
at time = 10 microseconds: Load volts= 93 millivolts
at time = 20 microseconds: Load volts= 366 millivolts
at
at
at
at
at
at
at
at
at
at
at
at
time
time
time
time
time
time
time
time
time
time
time
time
=
=
=
=
=
=
=
=
=
=
=
=
6,800.00
4,200.00
300.00
0.00
0.00
(10,000.00)
(2,600.00)
500.00
600.00
200.00
CostAccountingDriver
+main(args : String) : void
CostAccounting
-cash : Account
-rawMaterial : Account
-workInProcess : Account
-finishedGoods : Account
-wagesPayable : Account
-equity : Account
-sales : Account
-materialExpense : Account
-laborExpense : Account
-overheadExpense : Account
+getRawMaterial() : Account
+getWorkInProcess() : Account
+getFinishedGoods() : Account
+getWagesPayable() : Account
+invest(amount : double) : void
+purchase(amount : double) : void
+payWages() : void
+sell(job : Job, factor : double) : void
+balance() : void
Job
-accounts : CostAccounting
-material : double = 0.0
-labor : double = 0.0
-overhead : double = 0.0
-status : char = 'p'
+Job(accounts : CostAccounting,
material : double, labor : double,
overhead : double)
+finish() : void
+getMaterial() : double
+getLabor() : double
+getOverhead() : double
+getStatus() : char
+findCost() : double
+setStatus(status : char) : void
Account
-balance : double = 0
+debit(amount : double) : void
+credit(amount : double) : void
+getBalance() : double
if (job1.getStatus() == 'f')
{
accounts.sell(job1, 2.0);
}
if (job2.getStatus() == 'f')
{
accounts.sell(job2, 2.0);
}
// do trial balance
accounts.balance();
// work in process reconciliation
amount = 0;
if (job1.getStatus() == 'p')
{
amount += job1.findCost();
}
if (job2.getStatus() == 'p')
{
amount += job2.findCost();
}
System.out.println(
"\njob record of work in process = " + amount);
} // end main
In the CostAccounting class, all the instance variables should be references to objects of the
Account class. The invest method should debit cash and credits equity. The purchase
method should debit rawMaterial and credit cash. The payWages method should credit cash
and debit wagesPayable by the amount of wagesPayable. The sell method should set job
status to s, debit materialExpense by job material, debit laborExpense by job
labor, and debit overheadExpense by job overhead. It should credit finishedGoods by
the total job cost, and it should debit cash and credit sales by that amount times the factor
passed into sell as an argument. The balance method should use the printf method to round
money to the nearest penny. Insert commas and use parentheses for negative values.
In the Job class, the constructor should start a new job by storing a reference to the object that
contains references to all general accounts, and storing the amounts of individual job material, labor,
and overhead. The constructor also should debit workInProcess and credit rawMaterial by
the amount of material, and it should debit workInProcess and credit wagesPayable by the
total of labor and overhead. The finish method should credit workInProcess and debit
finishedGoods by the total job cost (material + labor + overhead), and it should set status to f.
8. [after 7.10] Political Campaign ***:
This is Chapter 3s Project 8 solved in a different way. By passing reference variables in arguments
we do a better job of modularizing the code and greatly simplify the main method. This project
includes several examples of overloaded methods and overloaded constructors.
Enter
Enter
Enter
Enter
Expected votes:
Regular party votes = 749
Regular independent votes = 416
Occasional party votes = 48
Other votes = 33
total expected votes = 1246
PoliticalCampaignDriver
Voters
-REGULAR_TURNOUT_FRACTION :
PoliticalCampaign
-DOLLARS_PER_SOLICITATION :
double = 5.00
-TELEPHONE_HOURS_PER_CALL :
double = 0.1
-total : Voters
-ourParty : Voters
-independent : Voters
-ourPartyRegulars : Voters
-independentRegulars : Voters
-ourPartyOccasionals : Voters
-others : Voters
-workers : Workers
-budget : double
-calls : int
+PoliticalCampaign()
+gatherData() : void
+evaluateProspects() : void
+budgetMoney() : void
+planTime() : void
double = 0.833
-maximum : int
-turnout : int
-favorable : double
+Voters()
+Voters(type : String)
+setMaximum(maximum : int) : void
+setTurnout(turnout : int) : void
+setTurnout() : void
+setTurnout(fraction : double) : void
+setFavorable(fraction : double) : void
+setFavorable(type : String) : void
+getMaximum() : int
+getTurnout() : int
+findVotes() : int
Workers
-CLERICAL_HOURS_PER_CALL :
double = 0.05
-TRANSPORT_HOURS_PER_CALL :
double = 0.05
-OVERHEAD_FRACTION : double = 0.25
-workers : int = 25
In class PoliticalCampaign, the constructor instantiates a Voter object for each Voter
reference variable. It uses the constructor with the String argument for all but the last two Voter
objects.
The gatherData method sets the turnout in total. It sets the maximum in
ourPartyOccasionals using the algorithm:
maximum ourParty maximum ourPartyRegulars maximum
It sets the maximum in others using the algorithm:
maximum total maximum ourPartyRegulars maximum
independentRegulars maximum ourPartyOccasionals maximum
It sets the favorable fraction in ourPartyRegulars, independentRegulars,
ourPartyOccasionals, and other. It sets the no-argument version to set turnout in
ourPartyRegulars and independentRegulars. It uses the double-argument version to
set turnout in ourPartyOccasionals and others, where the argument is: