Lab#SE01-1: Maven/Gradle Person and Account
Java SE Lab
1 Overview
Create a Maven/Gradle Java SE Project with three classes and Junit to test objects and operations.
💻 Lab#SE01-1: Maven/Gradle Person and Account tested by JUnit
Context
In order to complete this project, you will need to have a basic understanding of the Java programming language, as well as some familiarity with Maven or Gradle for managing dependencies and building the project.
Overall, this project will provide an opportunity for you to learn and apply the basics of Java programming, as well as gain experience with Maven or Gradle, JUnit, user input via the console and some discussion about composition and how classes work.
Additionally, you will need to have in mind that this project would center around a banking point of view.
By completing this project, you will have a starting foundation in these technologies and be able to build more complex and sophisticated Java applications in the future. You may go to Lab 2 (go Lab#SE01-2)
Goal
The goal of this project is to create three classes in Java (Person
, Account
and Manager
) that implement different algorithms or data structures, and to test them using JUnit.
These classes could include, for example, creating new objects, a data structure for storing and manipulating data (List
), or a utility class for performing common operations (static
).
Tasks
The tasks involved in this project include:
-
Decide where your project will weight:
Person
orAccount
. - Creating a new Maven or Gradle project and setting up the project structure.
-
Modifying the project’s
pom.xml
orbuild.gradle
file to import the necessary dependencies, including JUnit for testing. - Implementing the three required classes in Java, using appropriate algorithms and data structures.
- Implementing as well two basic pattern-designs: singleton and composition.
- Writing JUnit tests to verify that the classes work as expected.
You may attach the JUnit Test HTML results to documentation.
Optional
- As an optional task, you could also consider allowing the user to input data via the console, rather than using
hard-coded test data
in your JUnit tests.
This would allow you to test the classes with a variety of different input data, and to interact with the classes in a more dynamic way. - After mplementing two basic pattern-designs: singleton, composition your may think about factory.
2 Solving discussion
2.1 Base Classes
Here, the Person
class represents a person with a name, address and others. In the same way, Account
class is a bank account. The AccountManager
class contains static
methods to perform withdrawal
, transfer
, and change pin
operations on a Person
and Account
object.
public class Person {
private String name;
private String address;
// Other properties for a Person...
public Person(String name, String address) {
this.name = name;
this.address = address;
}
// Getters and setters for Person properties...
}
public class Account {
private String accountNumber;
private String pin;
private double balance;
// Other properties for an Account...
public Account(String accountNumber, String pin, double balance) {
this.accountNumber = accountNumber;
this.pin = pin;
this.balance = balance;
}
// Getters and setters for Account properties...
}
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;
}
}
2.2 Person has Account
public class Person {
private String name;
private String surname;
private int age;
private Account account;
public Person(String name, String surname, int age, Account account) {
this.name = name;
this.surname = surname;
this.age = age;
this.account = account;
}
public Account getAccount() {
return this.account;
}
public void setAccount(Account account) {
this.account = account;
}
// Getters and setters for Account properties...
}
2.3 Approach #1: Singleton
To use the Singleton design pattern with a Person and Account class, you could create a singleton AccountManager class
that manages the creation and operations of the Person
and Account
objects.
The AccountManager class
would have a private constructor
, to prevent multiple instances from being created, and a static getInstance
method that returns the singleton instance of the class.
The AccountManager class
would then have methods for performing various operations on the Person
and Account
objects, such as transferring money between accounts, withdrawing money from an account, or changing the PIN for an account.
These methods would be implemented using the Person
and Account
classes, and would be accessible to other classes through the singleton AccountManager
instance.
For example, you could define the AccountManager, Person, and Account classes as follows:
public class AccountManager {
private static AccountManager instance;
private Person person;
private Account account;
private AccountManager() {
// Private constructor to prevent multiple instances
}
public static AccountManager getInstance() {
if (instance == null) {
instance = new AccountManager();
}
return instance;
}
public void transfer(Account from, Account to, double amount) {
// Transfer money from one account to another
}
public void withdraw(Account account, double amount) {
// Withdraw money from an account
}
public void changePin(Account account, String newPin) {
// Change the PIN for an account
}
// Other methods for managing Person and Account objects...
}
To use the AccountManager
class, other classes would simply need to call the getInstance
method to obtain the singleton instance of the class, and then use the instance’s methods to perform operations on the Person
and Account
objects. For example:
// Create a new Person and Account
// be careful: where will these four Person objects go?
AccountManager manager = AccountManager.getInstance();
manager.person = new Person("John Doe", "123 Main St.");
manager.person = new Person("Carla Jameson", "323 Main St.");
manager.person = new Person("Rafael Martin", "3 Glorious St.");
manager.person = new Person("Pau Vila", "63 Sesamo St.");
But maybe, this is not the best approach for several reasons …
2.4 Approach #2: all static-methods AccountManager
It may work as follows. In fact, if we want to manage accounts (as a banking-centered problem, not a person-centered one), it could be better that Account
has Person
:
public class Account {
private Person person;
//other fields
public Account(Person person) {
this.person = person;
}
public Person getPerson() {
return this.person;
}
public void setPerson(Person person) {
this.person = person;
//constructor, getters, setters and methods
}
}
So, in this case, we could use a List
object to save all the Account
objects with the list:
import java.util.List;
import java.util.ArrayList;
public class AccountManager {
private List<Account> accounts;
private AccountManager() {
this.accounts = new ArrayList<>();
}
public List<Account> getAccounts() {
return this.accounts;
}
// we should manage how to add/remove accounts to/from accounts list
public static void deposit(Account account, double amount) {
// Code to deposit the specified amount to the account
}
public static void changePin(Account account, int newPin) {
// Code to change the PIN of the specified account
}
public static void transfer(Account fromAccount, Account toAccount, double amount) {
// Code to transfer the specified amount from the fromAccount to the toAccount
}
public static void withdrawal(Account account, double amount) {
// Code to withdraw the specified amount from the given account
}
}
2.5 Approach 3: Singleton, any static-method
In this approach:
Account
hasPerson
AccountManager
isSingleton
and there is no static-methods anywhere- and we create just one object form
AccountManager
to manageaccounts
import java.util.List;
import java.util.ArrayList;
public class AccountManager {
private static AccountManager manager = new AccountManager();
private List<Account> accounts;
// we should manage how to add/remove accounts to/from accounts list
private AccountManager() {
this.accounts = new ArrayList<>();
}
public static AccountManager getInstance() {
return manager;
}
public List<Account> getAccounts() {
return this.accounts;
}
public void deposit(Account account, double amount) {
// Code to deposit the specified amount to the account
}
public void changePin(Account account, int newPin) {
// Code to change the PIN of the specified account
}
public void transfer(Account fromAccount, Account toAccount, double amount) {
// Code to transfer the specified amount from the fromAccount to the toAccount
}
public void withdrawal(Account account, double amount) {
// Code to withdraw the specified amount from the given account
}
}
2.6 Test: AccountManagerTest
AccountManagerTest
could be like this:
- with
AccountManager
all static-methods no-singleton Account
hasPerson
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class AccountManagerTest {
@Test
public void testDeposit() {
Account account = new Account(new Person("John Doe"));
double initialBalance = account.getBalance();
double depositAmount = 100.00;
AccountManager.deposit(account, depositAmount);
double finalBalance = account.getBalance();
assertEquals(initialBalance + depositAmount, finalBalance);
}
@Test
public void testChangePin() {
Account account = new Account(new Person("John Doe"));
int initialPin = account.getPin();
int newPin = 1234;
AccountManager.changePin(account, newPin);
int finalPin = account.getPin();
assertEquals(newPin, finalPin);
}
@Test
public void testTransfer() {
Account fromAccount = new Account(new Person("John Doe"));
Account toAccount = new Account(new Person("Jane Doe"));
double initialFromAccountBalance = fromAccount.getBalance();
double initialToAccountBalance = toAccount.getBalance();
double transferAmount = 100.00;
AccountManager.transfer(fromAccount, toAccount, transferAmount);
double finalFromAccountBalance = fromAccount.getBalance();
double finalToAccountBalance = toAccount.getBalance();
assertEquals(initialFromAccountBalance - transferAmount, finalFromAccountBalance);
assertEquals(initialToAccountBalance + transferAmount, finalToAccountBalance);
}
@Test
public void testWithdrawal() {
Account fromAccount = new Account(new Person("John Doe"));
Account toAccount = new Account(new Person("Jane Doe"));
double initialFromAccountBalance = fromAccount.getBalance();
double withdrawalAmount = 100.00;
AccountManager.withdrawal(fromAccount, withdrawalAmount);
double finalFromAccountBalance = fromAccount.getBalance();
assertEquals(initialFromAccountBalance - withdrawalAmount, finalFromAccountBalance);
}
}
3 Step-by-step
- Create
Maven
Project with JUnit - Create
Person
class - Create
Account
class - Test
Person
andAccount
objects - Write operations (
withdrawal
,transfer
,change pin
) as astatic
methods inAccountManager
- Test
Person
andAccount
objects and features - Add
singleton
pattern toAccountManager
class - Test
AccountManager
class