This week the text introduces counting and conditional looping structures. Basically put, we'll use a counting loop (for) when we know at design time the exact number of iterations the loop body should run. By contrast if the number of iterations of the loop body is determined at run time, then we'll use a conditional loop (either while or do/while). Please know that part of your grade will be based on which loop structure you choose to implement - be sure you know the difference and when each is appropriate.
We're going to practice writing both types of loops in our exercise as we attempt to build an amortization schedule for the payments on a car or house loan. We'll use conditional loops to force our user to enter a number within a specified range before moving on to use the counted loop to build the table.
To make things go a little quicker, I have provided a Starter File on Moodle, which includes the *complete* Loan class (you won't need to write any code inside this one). This will allow you to focus on the loops that we're learning about this week.
The class you will develop is named ScheduleBuilder, which will keep track of the Loan object under consideration and the actual table itself. In order to allow this object to build the table, we'll define methods to:
To better organize our code, we'll provide a set of private helper methods to:
Be sure to read through all items before you begin.
1. Download the Zipped started file and extract its contents to a location where you'll be able to find it later.
2. Use Eclipse to open this Java project.
3. Inside the Starter you'll find the shell of the ScheduleBuilder class. Go through each of the class members present and read the existing code and comments. There are a number of // TODO comments throughout the different classes giving you tips on what needs to be done. Go through the code, remove the existing //TODO comments and replace each one with one or more Java statements that will perform the task. Pay close attention to existing code and comments as they may give you a hint as to how to model your code.
4. Create a package named edu.westga.cs6311.payments.controller. Add a new class named PaymentController to this package. This class should declare *only* the following instance variables:
5. Define a constructor that accepts no parameters and instantiates the Scanner object. This constructor should also assign null to the ScheduleBuilder and zero to the numeric instance variables.
6. Define a method named inputLoanData, which is a void method that accepts no parameters. Inside this method prompt the user for the Loan's initial balance, interest rate, and number of years. Use the Scanner object's nextLine method to read in the values and convert them to the appropriate data types. Be sure to include code so that if the user's input is outside the acceptable range (at least 0.0 for the balance, a rate between 0.0 and 1.0, inclusive, and a number of years that is greater than 0), then continue to prompt them for another value until it is within this range. Only when the user finally enters an acceptable value should this method store that value into the appropriate instance variables.
NOTE: We are still assuming that the user will enter the appropriate data type - we just don't know what actual value they might enter.
7. Define a method named createSchedule, which is a void method that accepts no parameters. Inside this method use the user's input values to create a Loan object and use that object to create the ScheduleBuilder object.
8. Define a method named buildTable, which is a void method that accepts no parameters. This method will use the ScheduleBuilder object to have the results table built.
9. Define a method named displayTable, which is a void method that accepts no parameters. This method will contain the code to display to the console the value returned from the ScheduleBuilder's getResults method.
10. Finally, add a new class named LoanDriver to the controller package. This new class should define a main method, which will create a PaymentController object and use it to get the user's input values, set up the ScheduleBuilder object, build the table, and display the results to the console.
ScheduleBuilder
package edu.westga.cs6311.payments.model;
/**
* ScheduleBuilder build a String that represents the amortization
* schedule of loan balances over a period of years.
*
*/
public class ScheduleBuilder {
private String theResults;
private Loan theLoan;
/**
* Builds a new ScheduleBuilder object to build the amortization
* schedule for the Loan specified
* @param theLoan The Loan to get the amortization schedule
*/
public ScheduleBuilder(Loan theLoan) {
// Initialize theResults as an empty String
theResults = "";
// Initialize theLoan to the value specified
this.theLoan = theLoan;
}
/**
* Builds the results table for the specified Loan
*/
public void buildTable() {
// Call helper methods buildHeading and buildYears
buildHeading();
buildYears();
}
/**
* Returns a String containing the results table for the Loan
*
* @return The full table
*/
public String getResults() {
return this.theResults;
}
/**
* Helper method that will build the table's heading
*/
private void buildHeading() {
this.theResults = "nMonth ";
this.theResults += this.headingFormat("Payment");
this.theResults += this.headingFormat("Principal");
this.theResults += this.headingFormat("Interest");
this.theResults += this.headingFormat("Balance");
this.theResults += "n";
}
/**
* This method will go through each of the years in the amortization schedule
* and call the method to build one year at a time
*/
private void buildYears() {
//TODO: Write a counting loop header that will be used to enclose the
// loop body shown in the next statement (this.theResults += ...)
// Be sure to:
// * initialize the yearNumber as an int with value 1
// * keep looping as long as yearNumber is less or equal to the number of years in the loan
// * increment yearNumber after each iteration
//
}
/**
* This method is used to build the amortization schedule for the year specified
* @param yearNumber This is the year being calculated for the table
* @return The String holding the formatted data for the year specified
*/
private String buildNextYear(int yearNumber) {
String theYear = "";
// TODO: Write the counting loop header that will be used to enclose the
// loop body shown in the following series of statements listing the
// month number, monthly payment, principal applied, interest applied, and new balance
// Be sure to:
// * initialize the monthNumber as an int with value 1
// * keep looping as long as monthNumber is less than or equal to 12
// * increment monthNumber after each iteration
}
/**
* This helper method is used to format the value as currency. It makes the table
* look nicer
* @param value The value to be formatted as currency
* @return The value formatted as currency
*/
private String currencyFormat(double value) {
return String.format("$%-11.2f", value);
}
/**
* This helper method is used to format the value as a heading to fit in the correct
* number of spaces
* @param value The value to be formatted
* @return The value formatted for a heading
*/
private String headingFormat(String value) {
return String.format("%-12s", value);
}
}
Loan.java
package edu.westga.cs6311.payments.model;
/**
* Models a loan account that requires a monthly payment
*
* @author CS6311
* @version Fall 2021
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ~~ DO NOT MODIFY THE CONTENTS OF THIS FILE AT ALL ~~
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
public class Loan {
private double originalBalance;
private double rate;
private int numberOfYears;
/**
* Constructor used to initialize the Account object
*
* @param originalBalance The initial loan balance
* @param rate The annual interest rate between 0 (0%) and 1 (100%)
* @param numberOfYears The loan's term in years
*/
public Loan(double originalBalance, double rate, int numberOfYears) {
if (originalBalance < 0) {
originalBalance = 0;
}
if (rate < 0 || rate > 1) {
rate = 0;
}
if (numberOfYears <= 0) {
numberOfYears = 1;
}
this.originalBalance = originalBalance;
this.rate = rate;
this.numberOfYears = numberOfYears;
}
/**
* Returns the term of the loan in years
* @return The term of the loan in years
*/
public int getTermInYears() {
return this.numberOfYears;
}
/**
* This method calculates the monthly payment for this loan
* @return The fixed monthly payment for this loan
*/
public double getMonthlyPayment() {
double numerator = this.rate / 12 * this.originalBalance;
double denominator = 1 - Math.pow(1 + this.rate / 12, -this.numberOfYears * 12);
return numerator / denominator;
}
/**
* This method returns the interest amount due for the specified
* month in the payment schedule
* @param month The month in which the interest is to be calculated
* 0 < month < this.numberOfYears * 12
* @param year The year in which the interest is to be calculated
* 0 < year < the loan's term in years
* @return The amount of interest due on the month, year specified
*/
public double getInterestOn(int month, int year) {
return this.rate / 12 * this.getBalanceOn(month, year);
}
/**
* Returns the remaining loan balance on the month specified
* @param month The month to find the balance due
* 0 < month < this.numberOfYears * 12
* @param year The year in which the interest is to be calculated
* 0 < year < the loan's term in years
* @return The remaining balance on the month, year specified
*/
public double getBalanceOn(int month, int year) {
int currentMonth = (year - 1) * 12 + month;
double onePlusMonthlyRate = 1 + this.rate / 12;
double numerator = this.originalBalance * (Math.pow(onePlusMonthlyRate, this.numberOfYears * 12)
- Math.pow(onePlusMonthlyRate, currentMonth));
double denominator = Math.pow(onePlusMonthlyRate, this.numberOfYears * 12) - 1;
return numerator / denominator;
}
}