(Game: ATM machine) Use the Account class created in Programming Exercise
9.7 to simulate an ATM machine. Create ten accounts in an array with id
0, 1, . . . , 9, and initial balance 100ドル. The system prompts the user to
enter an id. If the id is entered incorrectly, ask the user to enter a correct
id. Once an id is accepted, the main menu is displayed as shown in the sample
run. You can enter a choice 1 for viewing the current balance, 2 for
withdrawing money, 3 for depositing money, and 4 for exiting the main menu.
Once you exit, the system will prompt for an id again. Thus, once the system
starts, it will not stop.
Solution Code:
import java.util.Scanner;
public class Test {
private static Account[] accountsArr = new Account[10];
public static void main(String[] args) {
// initialize the array
for (int i = 0; i < accountsArr.length; i++) {
accountsArr[i] = new Account(i, 100);
}
Scanner input = new Scanner(System.in);
while (true) {
boolean found = false;
System.out.println("Enter the id");
int id = input.nextInt();
while (!found) {
for (int i = 0; i < accountsArr.length; i++) {
// Do a linear search to find whether the accountArr contains id
if (accountsArr[i].getId() == id) {
found = true;
break;
}
}
if (found) {
break;
} else {
System.out.println("Enter the correct id");
id = input.nextInt();
}
}
int choice;
do {
printMenu();
System.out.printf("Enter a choice ");
choice = input.nextInt();
switch (choice) {
case 1:
check_balance(id);
break;
case 2:
System.out.println("Enter an amount to withdraw");
int amount = input.nextInt();
withdraw(id, amount);
break;
case 3:
System.out.println("Enter an amount to deposit");
amount = input.nextInt();
deposit(id, amount);
break;
case 4:
break;
}
} while (choice != 4);
}
}
public static void check_balance(int id) {
System.out.println("The balance is " + accountsArr[id].getBalance());
}
public static void withdraw(int id, int money) {
accountsArr[id].setBalance(accountsArr[id].getBalance() - money);
}
public static void deposit(int id, int money) {
accountsArr[id].setBalance((accountsArr[id].getBalance() + money));
}
public static void printMenu() {
System.out.printf("Main Menu");
System.out.printf("\n1: Check balance\n2: Withdraw\n3: Deposit\n4: Exit\n");
}
}
Account.java for reference
import java.util.Date;
public class Account {
private int id;
private double balance;
private double annualInterestRate;
private Date date;
Account() {
id = 0;
balance = 0;
annualInterestRate = 0;
date = new Date();
}
Account(int num1, double num2) {
id = num1;
balance = num2;
}
/**
* setters */ public void setId(int num) {
id = num;
}
public void setBalance(double num) {
balance = num;
}
public void setAnnualInterestRate(double num) {
annualInterestRate = num;
}
/**
* getters-dateCreated also has a getter */ public int getId() {
return id;
}
public double getBalance() {
return balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public double getMonthlyInterest() {
return (balance * annualInterestRate / 1200);
}
public Date getDateCreate() {
return date;
}
/**
* withdraw method and deposit method */ public void deposit(double num) {
balance += num;
}
public void withdraw(double num) {
if (num > 0 && num < balance) {
balance -= num;
}
}
}
My concern is I am not really following Object Oriented Principles in this program design. I am currently reading daniel liang's java textbook and through the chapter "Object Oriented Thinking". I am solving its exercises which I assume want to teach me OOPs concepts. They want to make me write earlier programs that I wrote functionally using Object Oriented way.
However, that's the most object oriented code that I can write at this moment with my knowledge. Can anyone here guide me by reviewing my code and instructing me to check some useful resources on OOPs design thinking?
1 Answer 1
Overall Design
Test
class (not a good name by the way) violates the Single responsibility principle. It mixes business logic with the concerns of a command line UI.
Encapsulation
You should always prefer using domain-specific behavior instead of dumb setters.
And you should never blindly expose getters and especially setters for each and every class field.
OOP is about exposing behavior, not data.
Remove setBalance()
from your Account
class, use withdraw()
and deposit()
methods instead of it.
Also, your withdraw()
implementation doesn't communicate to the caller if the transaction succeeded. If the balance is insufficient, it silently exits. Consider defining a custom exception to be thrown is such a case.
Don't use double
for Monetary values
You should never use binary floating point type for describing monetary values. If never heard this suggestion and wonder why, run the following piece of code:
double result = 0;
for (int i = 0; i < 10; i++) {
result += 0.1;
}
System.out.println(result); // 0.9999999999999999
It will not give the expected value of 1
. Floating point arithmetic is inaccurate, any operation on double
values might result in a precision loss. That something you don't want to happen while dealing with money.
Instead, use BigDecimal
to represent monetary values.
Avoid using legacy types
With the inception of Java 8 java.util.Date
was superseded java.time.Instant
which represents a specific moment on the timeline in UTC.
Since Instant
is UTC-based, it's a good choice for documenting events in a banking application (in a real banking app the data is stored on the server side, not on the client). And in a real application, if you need to display the time to the user, you'll have to apply the user's time zone to the Instant
, which can be done using Instant.atZone()
passing ZoneId.systemDefault()
as an argument.
Don't use legacy date-time classes, they are still there only for backward compatibility reasons.
Naming
The names of the code elements should communicate their responsibilities.
num
, date
are not good names, because they don't tell much to the code reader what they are meant to represent (which date, account-activation, creation, suspension?)
Account
class are not used in the test function. For some references on Object Oriented programming see the SOLID principles. Use ArrayList rather than an array for the accounts. \$\endgroup\$while (!found)
should be a helper method with an early boolean return, that'd save you a flag and twobreak
s, improving readability and making code flow more obvious. Similarly,main
is doing too much directly: it creates new accounts and handles CLI input in a loop, so you can't easily test the behaviour "given user has id 2 selected and presses 3". And you're probably missing a newline after doc comments in Account.java \$\endgroup\$