New to Java, just finished the Helsinki MOOC course. I programmed a basic mortgage calculator as my first solo project and while it works, I have a few questions about coding style:
The listener and ui classes are cluttered with variables at the top. Is this acceptable or is there a cleaner way of organizing it?
Is initializing the variables in the
CalcLogic
class as strings and then creating newBigDecimal
variables in the methods an accepted way of usingBigDecimal
, or would it better to initialize the variables as BigDecimals?Are there any other coding style or logic issues that are noticeable?
CalcLogic
class:
package mortgagecalculator.logic;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* Logic for mortgage calculator.
* Input variables for home value, loan term, etc. and outputs EMI, total payment, etc.
*
*/
public class CalcLogic {
//Variables for calculating mortgage. propTax, homeIns, etc are not initialized in constructor
private String principal = "0";
private String downPayment = "0";
private String interestRate = "0";
private int loanTerm = 0;
private String propTax = "0";
private String homeIns = "0";
private String hoaFee = "0";
private String pmiRate = "0";
public void setPrincipal(String principal) {
this.principal = principal;
}
public void setDownPayment(String downPayment) {
this.downPayment = downPayment;
}
public void setInterest(String interestRate) {
this.interestRate = interestRate;
}
public void setLoanTerm(int loanTerm) {
this.loanTerm = loanTerm;
}
public void setPropTax(String propTax) {
this.propTax = propTax;
}
public void setHomeIns(String homeIns) {
this.homeIns = homeIns;
}
public void setHOAfee(String hoaFee) {
this.hoaFee = hoaFee;
}
public void setPMIrate(String pmiRate) {
this.pmiRate = pmiRate;
}
//Returns principal minus downpayment
public BigDecimal getInitialAmount() {
return new BigDecimal(this.principal).subtract(new BigDecimal(this.downPayment));
}
//Returns twenty percent of principal
public BigDecimal twentyDown() {
BigDecimal twenty = new BigDecimal("20").divide(new BigDecimal("100"));
return new BigDecimal(this.principal).multiply(twenty);
}
public BigDecimal monthlyInterest() {
return new BigDecimal(this.interestRate).divide(new BigDecimal("100"))
.divide(new BigDecimal("12"), 8, RoundingMode.UP);
}
public BigDecimal monthlyPMI() {
try {
BigDecimal result = getInitialAmount().divide(new BigDecimal(this.pmiRate).divide(new BigDecimal("100"))
.divide(new BigDecimal("12")), 2, RoundingMode.UP);
return result;
} catch (Exception e) {
return BigDecimal.ZERO;
}
}
public BigDecimal additionalTax() {
BigDecimal sum = new BigDecimal(this.propTax);
sum = sum.add(new BigDecimal(this.homeIns)).add(new BigDecimal(this.hoaFee));
return sum;
}
public BigDecimal getEMI() {
BigDecimal value = monthlyInterest().add(BigDecimal.ONE);
value = value.pow(this.loanTerm * 12);
BigDecimal numerator = value.multiply(monthlyInterest()).multiply(getInitialAmount());
BigDecimal denominator = value.subtract(BigDecimal.ONE);
return numerator.divide(denominator, 2, RoundingMode.UP);
}
public BigDecimal monthlyPayment() {
BigDecimal result = getEMI().add(monthlyPMI()).add(additionalTax());
return result;
}
public BigDecimal totalInterest() {
BigDecimal result = getEMI().multiply(new BigDecimal(this.loanTerm * 12));
result = result.subtract(getInitialAmount());
return result;
}
//Returns month where principal payments first exceed interest payments
public int amortMonth() {
BigDecimal remaining = getInitialAmount().setScale(2, RoundingMode.CEILING);
for (int i = 0; i < this.loanTerm * 12; i++) {
BigDecimal interPay = remaining.multiply(monthlyInterest()).setScale(2, RoundingMode.CEILING);
BigDecimal princPay = getEMI().subtract(interPay).setScale(2, RoundingMode.CEILING);
if (princPay.compareTo(interPay) == 1) {
return i + 1;
}
remaining = remaining.subtract(princPay).setScale(2, RoundingMode.CEILING);
}
return 0;
}
}
CalcListener
class:
package mortgagecalculator.ui;
import mortgagecalculator.logic.CalcLogic;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.math.BigDecimal;
public class CalcListener implements ActionListener {
private JTextField principal;
private JTextField downPayment;
private JTextField interestRate;
private JTextField loanTerm;
private JTextField propTax;
private JTextField homeIns;
private JTextField hoaFee;
private JTextField pmiRate;
private JButton button;
private JTextField EMI;
private JTextField monthlyFee;
private JTextField totalInt;
private JTextField changeMonth;
private CalcLogic calc;
public CalcListener(JTextField principal, JTextField downPayment, JTextField interestRate,
JTextField loanTerm, JTextField propTax, JTextField homeIns,
JTextField hoaFee, JTextField pmiRate, JTextField EMI,
JTextField monthlyFee, JTextField totalInt, JTextField changeMonth, JButton button) {
this.principal = principal;
this.downPayment = downPayment;
this.interestRate = interestRate;
this.loanTerm = loanTerm;
this.propTax = propTax;
this.homeIns = homeIns;
this.hoaFee = hoaFee;
this.pmiRate = pmiRate;
this.button = button;
this.EMI = EMI;
this.monthlyFee = monthlyFee;
this.totalInt = totalInt;
this.changeMonth = changeMonth;
this.calc = new CalcLogic();
}
@Override
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == this.button) {
this.calc.setPrincipal(this.principal.getText());
this.calc.setDownPayment(this.downPayment.getText());
this.calc.setInterest(this.interestRate.getText());
this.calc.setLoanTerm(Integer.parseInt(this.loanTerm.getText()));
this.calc.setPropTax(this.propTax.getText());
this.calc.setHomeIns(this.homeIns.getText());
this.calc.setPMIrate(this.pmiRate.getText());
this.calc.setHOAfee(this.hoaFee.getText());
this.EMI.setText(this.calc.getEMI().toString());
this.monthlyFee.setText(this.calc.monthlyPayment().toString());
this.totalInt.setText(this.calc.totalInterest().toString());
this.changeMonth.setText(String.valueOf(this.calc.amortMonth()));
}
}
}
UserInterface
class:
package mortgagecalculator.ui;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.*;
import mortgagecalculator.ui.CalcListener;
public class UserInterface implements Runnable {
private JFrame frame;
private JTextField principal;
private JTextField downPayment;
private JTextField interestRate;
private JTextField loanTerm;
private JTextField propTax;
private JTextField homeIns;
private JTextField hoaFee;
private JTextField pmiRate;
private JButton button;
private JTextField EMI;
private JTextField monthlyFee;
private JTextField totalInt;
private JTextField changeMonth;
@Override
public void run() {
frame = new JFrame("Mortgage Calculator");
frame.setPreferredSize(new Dimension(1000, 1000));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
createComponents(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
private void createComponents(Container container) {
generateVariables();
container.setLayout(new GridLayout(3, 1));
container.add(createInputPanel());
container.add(this.button);
container.add(createOutputPanel());
}
private void generateVariables() {
this.principal = new JTextField("");
this.downPayment = new JTextField("");
this.interestRate = new JTextField("");
this.loanTerm = new JTextField("");
this.propTax = new JTextField("");
this.homeIns = new JTextField("");
this.hoaFee = new JTextField("");
this.pmiRate = new JTextField("");
this.EMI = new JTextField("");
this.monthlyFee = new JTextField("");
this.totalInt = new JTextField("");
this.changeMonth = new JTextField("");
this.button = new JButton("Calculate!");
CalcListener calc = new CalcListener(this.principal, this.downPayment, this.interestRate,
this.loanTerm, this.propTax, this.homeIns, this.hoaFee,
this.pmiRate, this.EMI, this.monthlyFee, this.totalInt,
this.changeMonth, this.button);
this.button.addActionListener(calc);
}
private JPanel createInputPanel() {
JPanel panel = new JPanel(new GridLayout(4, 4));
panel.add(new JLabel("Principal"));
panel.add(new JLabel("Down Payment"));
panel.add(new JLabel("Interest Rate (in %)"));
panel.add(new JLabel("Loan term (in years)"));
panel.add(this.principal);
panel.add(this.downPayment);
panel.add(this.interestRate);
panel.add(this.loanTerm);
panel.add(new JLabel("Property Tax (Monthly)"));
panel.add(new JLabel("Home Insurance (Monthly)"));
panel.add(new JLabel("HOA Fees (Monthly)"));
panel.add(new JLabel("PMI Rate (in %)"));
panel.add(this.propTax);
panel.add(this.homeIns);
panel.add(this.hoaFee);
panel.add(this.pmiRate);
return panel;
}
private JPanel createOutputPanel() {
JPanel panel = new JPanel(new GridLayout(4, 2));
panel.add(new JLabel("Equated Monthly Installment"));
panel.add(new JLabel("Monthly w/ fees"));
panel.add(this.EMI);
panel.add(this.monthlyFee);
panel.add(new JLabel("Total Interest Payments"));
panel.add(new JLabel("Month where principal payments > interest"));
panel.add(this.totalInt);
panel.add(this.changeMonth);
return panel;
}
public JFrame getFrame() {
return frame;
}
}