I have a couple questions about this program
Should I implement runnable instead of using thread class? I like the idea of the consumer and producer being thread objects, it seems simpler to me, but I have seen people argue the benefits if runnable interface.
Are static methods in the main class considered good/bad convention?
For a factory pattern like this, is the parent of the two bank accounts the best place to put the multithreading logic?
Should setters be included always? Or should they be left out if the constructor is the only method that should set values? Is one of these approaches bad practice?
Does an arraylist of accounts count as tight coupling and if so should it be abandoned? If so, what is the best way for a bank object to have access to each account?
Thank you in advance
bank class:
package Model;
import java.util.ArrayList;
public class Bank {
private ArrayList<BankAccount> accounts;
private static int numberOfBanks;
private int routingNumber;
public Bank() {
this.accounts = new ArrayList<BankAccount>();
this.routingNumber = ++numberOfBanks;
}
}
BankAccount.java:
package Model;
import java.util.Date;
abstract class BankAccount {
static private int numberOfAccounts;
private int accountNumber;
private double balance;
private String accountHolder;
private Date accountOpened;
private int withdrawsLeft;
public BankAccount(String name) {
this.accountNumber = ++numberOfAccounts;
this.balance = 0;
this.accountHolder = name;
this.accountOpened = new Date();
}
public BankAccount(String name, double initialBalance) {
this.balance = initialBalance;
this.accountHolder = name;
this.accountOpened = new Date();
}
public static int getNumberOfAccounts() {
return numberOfAccounts;
}
public static void setNumberOfAccounts(int numberOfAccounts) {
BankAccount.numberOfAccounts = numberOfAccounts;
}
public int getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(int accountNumber) {
this.accountNumber = accountNumber;
}
public synchronized double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public String getAccountHolder() {
return accountHolder;
}
public void setAccountHolder(String accountHolder) {
this.accountHolder = accountHolder;
}
public Date getAccountOpened() {
return accountOpened;
}
public void setAccountOpened(Date accountOpened) {
this.accountOpened = accountOpened;
}
public int getWithdrawsLeft() {
return withdrawsLeft;
}
public void setWithdrawsLeft(int withdrawsLeft) {
this.withdrawsLeft = withdrawsLeft;
}
public synchronized void deposit(double d) {
try {
Thread.sleep(300);
}
catch(InterruptedException e) {
e.getMessage();
}
balance += d;
printReceipt();
}
public synchronized void withdraw(double w) {
while (w > balance) {
try {
wait(); // wait for funds
} catch (InterruptedException ie) {
System.err.println(ie.getMessage());
}
}
if (balance > 0) {
balance -= w;
printReceipt();
}
else System.out.println("ERROR: You are way too broke for that!");
}
public void printReceipt() {
System.out.println(getAccountHolder() + "\'s balance is " + getBalance() + "0 on " + accountOpened.toString());
}
}
Main.java:
package Model;
public class Main {
public static void main(String[] args) {
Bank bankOfJames = new Bank();
BankAccountFactory fact = new BankAccountFactory();
BankAccount james = fact.getAccount("savings account","james", 1000.0);
Thread[] users = new Thread[10];
int[] threadNames = new int[10];
for(int i = 0; i < users.length; i++) {
if(i % 2 == 0) users[i] = new Producer(james);
else users[i] = new Consumer(james);
}
for(int i = 0; i < users.length; i++) {
System.out.println("Starting thread " + users[i].getName());
users[i].start();
}
for(int i = 0; i < users.length; i++) {
try {
System.out.println("Joining thread " + users[i].getName());
users[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static class Producer extends Thread{
BankAccount a;
Producer(BankAccount ba){
this.a = ba;
}
public void run() {
a.deposit(100);
}
}
static class Consumer extends Thread{
BankAccount a;
Consumer(BankAccount ba){
this.a = ba;
}
public void run() {
a.withdraw(50);
}
}
}
CheckingAccount.java:
package Model;
public class CheckingAccount extends BankAccount {
public CheckingAccount(String name) {
super(name);
// TODO Auto-generated constructor stub
}
public CheckingAccount(String name, double initialBalance) {
super(name, initialBalance);
// TODO Auto-generated constructor stub
}
public void writeCheck() {
System.out.println("writing check");
}
}
SavingsAccount.java:
package Model;
public class SavingsAccount extends BankAccount {
private double interestRate;
public double getInterestRate() {
return interestRate;
}
public void setInterestRate(double interestRate) {
this.interestRate = interestRate;
}
public SavingsAccount(String name) {
super(name);
// TODO Auto-generated constructor stub
}
public SavingsAccount(String name, double initialBalance) {
super(name, initialBalance);
// TODO Auto-generated constructor stub
}
public void compoundInterest() {
double current = getBalance();
current *= interestRate;
setBalance(current);
}
}
BankAccountFactory.java
package Model;
public class BankAccountFactory {
public BankAccount getAccount(String account, String name) {
if(account.equalsIgnoreCase("checkingaccount") || account.equalsIgnoreCase("checking account")) {
return new CheckingAccount(name);
}
else if(account.equalsIgnoreCase("savingsaccounts") || account.equalsIgnoreCase("savings account")) {
return new SavingsAccount(name);
}
else{
System.out.println("Please indicate savings or checking account");
return null;
}
}
public BankAccount getAccount(String account, String name, double balance) {
if(account.equalsIgnoreCase("checkingaccount") || account.equalsIgnoreCase("checking account")) {
return new CheckingAccount(name, balance);
}
else if(account.equalsIgnoreCase("savingsaccounts") || account.equalsIgnoreCase("savings account")) {
return new SavingsAccount(name, balance);
}
else{
System.out.println("Please indicate savings or checking account");
return null;
}
}
}