LAB#SE01-1: Maven Person and Account

javase
lab

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.

  1. Create a new Maven or Gradle project and setting up the project structure
  2. Modify the project’s pom.xml or build.gradle file to import necessary dependencies, including JUnit for testing
  3. Implement the three required classes in Java
  4. Implement two basic patter-designs: singleton and think about factory
  5. 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

Note
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"

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

Note
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

Note
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);
    }
}