classDiagram Person "1" --o "1" Account : has Person "*" -- "1" AccountManager : manages class Person{ -String name -String address -int age -Account account +getBalance() +setBalance() +getPin() +setPin() } class Account{ -String accountManager -String pin -double balance +equals(Account compared) boolean } class AccountManager{ +withdrawal(Person person, double amount) boolean +transfer(Person sender, Person receiver, double amount) boolean +changePin(Person person, String oldPin, String newPin) boolean } <<Static>> AccountManager %%note for AccountManager "Can withdraw money from a person's account\nCan tranfer money between two persons' account\nCan change the Pin of a person's account"
LAB#SE01-1: Maven Person and Account
Maven Person and Account
Basic understanding of Java programming language is required, as well as some familiarity with Maven or Gradle for managing dependencies and building the project.
Knowledge of algotighms and data structures to implement the required classes.
Create three classes in Java (Person
, Account
and Manager
) that implement different algorightms or data structures.
Test these classes using JUnit.
- Create a new Maven or Gradle project and setting up the project structure
- Modify the project’s
pom.xml
orbuild.gradle
file to import necessary dependencies, including JUnit for testing - Implement the three required classes in Java
- Implement two basic patter-designs: singleton and think about factory
- Write JUnit tests to verify that classes work as expected
- Allow the user to input data via the console, rather than using
hard-coded test data
in JUnit tests
Approach 1
UML diagram
Classes specification
Person
package org.labSe01Part1;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter @Setter @NoArgsConstructor
public class Person {
private String name;
private String address;
private int age;
private Account account;
public Person(String name, String address, int age, Account account) {
this.name = name;
this.address = address;
this.age = age;
this.account = account;
}
public double getBalance() {
return this.account.getBalance();
}
public void setBalance(double amount) {
this.account.setBalance(amount);
}
public String getPin() {
return this.account.getPin();
}
public void setPin(String newPin) {
this.account.setPin(newPin);
}
}
Account
package org.labSe01Part1;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter @Setter @NoArgsConstructor
public class Account {
private String accountNumber;
private String pin;
private double balance;
public Account(String accountNumber, String pin, double balance) {
this.accountNumber = accountNumber;
this.pin = pin;
this.balance = balance;
}
public boolean equals(Object compared) {
if (this == compared) {
return true;
}
if (!(compared instanceof Account)) {
return false;
}
Account comparedAccount = (Account) compared;
if (this.accountNumber.equals(comparedAccount.accountNumber) &&
this.pin.equals(comparedAccount.pin) &&
this.balance == comparedAccount.balance) {
return true;
}
return false;
}
}
AccountManager
package org.labSe01Part1;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter @Setter @NoArgsConstructor
/**
* Public class with static methods to manage accounts between persons
*/
public class AccountManager {
public static boolean withdrawal(Person person, double amount) {
if (amount > 0 && amount <= person.getBalance()) {
person.setBalance(person.getBalance() - amount);
return true;
}
return false;
}
public static boolean transfer(Person sender, Person receiver, double amount) {
if (amount > 0 && amount <= sender.getBalance()) {
sender.setBalance(sender.getBalance() - amount);
receiver.setBalance(receiver.getBalance() + amount);
return true;
}
return false;
}
public static boolean changePin(Person person, String oldPin, String newPin) {
if (person.getPin().equals(oldPin)) {
person.setPin(newPin);
return true;
}
return false;
}
}
Approach 2
The AccountManager
class is created as a Singleton
to force its usage when managing accounts.
UML diagram
classDiagram class Account { -String accountNumber -String pin -double balance +getAccountNumber() +setAccountNumber() +getPin() +setPin() +getBalance() +setBalance() } class Person { -String name -int age -String address -Account account +getName() +setName() +getAge() +setAge() +getAddress() +setAddress() +getAccount() +setAccount() +getBalance() +setBalance() +getPin() +setPin() } %% AccountManager Singleton class AccountManager { +getInstance() +withdrawal(Person client, double amount) +transfer(Person sender, Person receiver, double amount) +deposit(Person client, double amount) +changePin(Person client) } <<Singleton>> AccountManager %% Classes relationships Person "1" --o "1" Account : has Person "*" -- "1" AccountManager : manages
Classes specifications
Person
package org.labSe01Part1;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter @Setter
public class Person {
private String name;
private String address;
private int age;
private Account account;
public Person(String name, String address, int age, Account account) {
this.name = name;
this.address = address;
this.age = age;
this.account = account;
}
public double getBalance() {
return this.account.getBalance();
}
public void setBalance(double amount) {
this.account.setBalance(amount);
}
public String getPin() {
return this.account.getPin();
}
public void setPin(String newPin) {
this.account.setPin(newPin);
}
}
Account
package org.labSe01Part1;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter @Setter
public class Account {
private String accountNumber;
private String pin;
private double balance;
public Account(String accountNumber, String pin, double balance) {
this.accountNumber = accountNumber;
this.pin = pin;
this.balance = balance;
}
public boolean equals(Object compared) {
if (this == compared) {
return true;
}
if (!(compared instanceof Account)) {
return false;
}
Account comparedAccount = (Account) compared;
if (this.accountNumber.equals(comparedAccount.accountNumber) &&
this.pin.equals(comparedAccount.pin) &&
this.balance == comparedAccount.balance) {
return true;
}
return false;
}
}
AccountManager
package org.labSe01Part1;
public class AccountManager {
private static AccountManager instance;
private AccountManager() {
// Empty constructor?
}
public static AccountManager getInstance() {
if (instance == null) {
instance = new AccountManager();
}
return instance;
}
public static boolean withdrawal(Person person, double amount) {
if (amount > 0 && amount <= person.getBalance()) {
person.setBalance(person.getBalance() - amount);
return true;
}
return false;
}
public static boolean transfer(Person sender, Person receiver, double amount) {
if (amount > 0 && amount <= sender.getBalance()) {
sender.setBalance(sender.getBalance() - amount);
receiver.setBalance(receiver.getBalance() + amount);
return true;
}
return false;
}
public static boolean deposit(Person person, double amount){
if (amount > 0) {
person.setBalance(person.getBalance() + amount);
return true;
}
return false;
}
public static boolean changePin(Person person, String oldPin, String newPin) {
if (person.getPin().equals(oldPin)) {
person.setPin(newPin);
return true;
}
return false;
}
}
Approach 3 - Singleton + Hashmaps
Another approach would be that a client might have multiple accounts. We can manage all the client’s accounts as a HashMap
where its keys are the account’s owners (Person
) and each value is a list of accounts (Account
). This way we are decoupling classes Account and Person.
The AccountManager
class is created as a Singleton
to force its usage when managing accounts.
UML diagram
classDiagram class Person { -String name -int age -String address +getName() +setName() +getAge() +setAge() +getAddress() +setAddress() +equals(Person compared) boolean } class Account { -String accountNumber -String pin -double balance +getAccountNumber() +setAccountNumber() +getPin() +setPin() +getBalance() +setBalance() +equals(Account compared) boolean } %% AccountManager Singleton class AccountManager { -HashMap< Person, ArrayList< Account > > clients -getAccount(Person client, String accountNumber) -createAccount(Person client, Account newAccount) -removeAccount(Person client, Account newAccount) -getClient(String clientName) -removeClient(Person client) -getAccounts(Person client) -changePin(Account account, String oldPin, String newPin) +getInstance() +getClientBalance(String clientName) +getAccountBalance(String clientName, String accountNumber) +withdrawal(String clientName, String accountNumber, double amount) +transfer(String senderName, String senderAccountNumber, String receiverName, String receiverAccountNumber, double amount) +deposit(String clientName, String accountNumber, double amount) +changePin(String clientName, String accountNumber, String oldPin, String newPin) } <<Singleton>> AccountManager %% Classes relationship Person "*" -- "1" AccountManager : manages Account "*" -- "1" AccountManager : manages
Classes specifications
Person
package org.labSe01Part1;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter @Setter
public class Person {
private String name;
private String address;
private int age;
public Person(String name, String address, int age) {
this.name = name;
this.address = address;
this.age = age;
}
}
Account
package org.labSe01Part1;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class Account {
private String accountNumber;
private String pin;
private double balance;
public Account(String accountNumber, String pin, double balance) {
this.accountNumber = accountNumber;
this.pin = pin;
this.balance = balance;
}
public boolean equals(Object compared) {
if (this == compared) {
return true;
}
if (!(compared instanceof Account)) {
return false;
}
Account comparedAccount = (Account) compared;
if (this.accountNumber.equals(comparedAccount.accountNumber) &&
this.pin.equals(comparedAccount.pin) &&
this.balance == comparedAccount.balance) {
return true;
}
return false;
}
}
AccountManager
package org.labSe01Part1;
import java.util.ArrayList;
import java.util.HashMap;
public class AccountManager {
private static AccountManager instance;
private HashMap<Person, ArrayList<Account>> clients;
private AccountManager() {
this.clients = new HashMap<>();
// Empty constructor?
}
public static AccountManager getInstance() {
if (instance == null) {
instance = new AccountManager();
}
return instance;
}
// clearAccounts only for testing purposes
public void clearAccounts() {
this.clients = new HashMap<>();
}
// Private methods: any method that requires Person or Account objects as input parameters
private boolean createAccount(Person client, Account newAccount) {
if (!this.clients.containsKey(client)) {
ArrayList<Account> accounts = new ArrayList<>();
accounts.add(newAccount);
this.clients.put(client, accounts);
return true;
}
else if (!this.clients.get(client).contains(newAccount)) {
this.clients.get(client).add(newAccount);
return true;
}
return false;
}
private boolean removeAccount(Person client, Account accountToDelete) {
if (this.clients.containsKey(client) && this.clients.get(client).contains(accountToDelete)) {
this.clients.get(client).remove(accountToDelete);
System.out.println("Account " + accountToDelete.getAccountNumber() + " removed from the system.");
return true;
}
return false;
}
private boolean removeClient(Person client) {
if (this.clients.containsKey(client)) {
this.clients.remove(client);
System.out.println("Client " + client.getName() + " removed from the system.");
return true;
}
return false;
}
private ArrayList<Account> getAccounts(Person client) {
return this.clients.getOrDefault(client, null);
}
private Person getClient(String clientName) {
for (Person client : this.clients.keySet()) {
if (client.getName().equals(clientName)) {
return client;
}
}
return null;
}
private Account getAccount(Person client, String accountNumber) {
if (this.clients.containsKey(client)) {
for (Account account : this.clients.get(client)) {
if (account.getAccountNumber().equals(accountNumber)) {
return account;
}
}
}
return null;
}
private double getClientBalance(Person client) {
double totalBalance = 0.0;
if (this.clients.containsKey(client)) {
for (Account account : this.clients.get(client)) {
totalBalance += account.getBalance();
}
}
return totalBalance;
}
private boolean withdrawal(Account account, double amount) {
// Check if there's enough money to withdraw
if (amount > 0 && amount <= account.getBalance()) {
account.setBalance(account.getBalance() - amount);
System.out.println("Withdrawal completed. New balance: " + account.getBalance());
return true;
}
// Not enough money to withdraw
System.out.println("Not enough money on client's account. Withdrawal operation cancelled.");
return false;
}
private boolean transfer(Account senderAccount, Account receiverAccount, double amount) {
// Check if we can transfer the money
if (amount > 0 && amount <= senderAccount.getBalance()) {
senderAccount.setBalance(senderAccount.getBalance() - amount);
receiverAccount.setBalance(receiverAccount.getBalance() + amount);
System.out.println("Transfer completed.");
System.out.println("New sender balance: " + senderAccount.getBalance());
System.out.println("New receiver balance: " + receiverAccount.getBalance());
return true;
}
// Not enough money for the transfer
System.out.println("Not enough money on sender's account. Transfer operation cancelled.");
return false;
}
private boolean deposit(Account account, double amount) {
if (amount > 0) {
account.setBalance(account.getBalance() + amount);
System.out.println("Deposit completed. New balance: " + account.getBalance());
return true;
}
System.out.println("Bad amount of money. Deposit cancelled.");
return false;
}
private boolean changePin(Account account, String oldPin, String newPin) {
if (account.getPin().equals(oldPin)) {
account.setPin(newPin);
System.out.println("Pin correctly changed.");
return true;
}
return false;
}
// Public methods: only basic data types accepted, no Person or Account
public boolean createAccount(String clientName, String clientAddress, int age, String accountNumber, String accountPin, double balance) {
return this.createAccount(new Person(clientName, clientAddress, age), new Account(accountNumber, accountPin, balance));
}
public boolean createAccount(String clientName, String clientAddress, int age, String accountNumber, String accountPin) {
return this.createAccount(clientName, clientAddress, age, accountNumber, accountPin, 0.0);
}
public boolean removeAccount(String clientName, String accountNumber) {
Person client = this.getClient(clientName);
if (client == null) {
return false;
}
Account account = this.getAccount(client, accountNumber);
if (account == null) {
return false;
}
return this.removeAccount(client, account);
}
public boolean removeClient(String clientName) {
Person client = this.getClient(clientName);
if (client == null) {
return false;
}
return this.removeClient(client);
}
public Account getAccount(String clientName, String accountNumber) {
Person client = this.getClient(clientName);
if (client == null) {
return null;
}
return this.getAccount(client, accountNumber);
}
public double getAccountBalance(String clientName, String accountNumber) {
Account account = this.getAccount(clientName, accountNumber);
if (account == null) {
return 0.0;
}
return account.getBalance();
}
public double getClientBalance(String clientName) {
Person client = this.getClient(clientName);
if (client == null) {
System.out.println("Client " + clientName + " does not exist.");
return 0.0;
}
return this.getClientBalance(client);
}
public boolean withdrawal(String clientName, String accountNumber, double amount) {
// Retrieve the account from that client
Account account = this.getAccount(clientName, accountNumber);
if (account == null) {
System.out.println("Client " + clientName + "doesn't have an account with number " + accountNumber +". Withdrawal operation cancelled.");
return false;
}
return this.withdrawal(account, amount);
}
public boolean transfer(String senderName, String senderAccountNumber, String receiverName, String receiverAccountNumber, double amount) {
// Retrieve the sender account
Account senderAccount = this.getAccount(senderName, senderAccountNumber);
if (senderAccount == null) {
System.out.println("Client " + senderName + "doesn't have an account with number " + senderAccountNumber + ". Transfer operation cancelled.");
return false;
}
// Retrieve the receiver account
Account receiverAccount = this.getAccount(receiverName, receiverAccountNumber);
if (receiverAccount == null) {
System.out.println("Client " + senderName + "doesn't have an account with number " + senderAccountNumber + ". Transfer operation cancelled.");
return false;
}
return this.transfer(senderAccount, receiverAccount, amount);
}
public boolean deposit(String clientName, String accountNumber, double amount){
Account clientAccount = this.getAccount(clientName, accountNumber);
if (clientAccount == null) {
System.out.println("Client " + clientName + " doesn't have an account with number " + accountNumber + ". Deposit operation cancelled.");
return false;
}
return this.deposit(clientAccount, amount);
}
public boolean changePin(String clientName, String accountNumber, String oldPin, String newPin) {
Account account = this.getAccount(clientName, accountNumber);
if (account == null) {
return false;
}
return this.changePin(account, oldPin, newPin);
}
}