Java Exercise Problem: Read transactions from a file and apply them to an account

The Problem

Create a program that maintains a bank account. The program uses a Account class with pertinent properties for account number, holder name, account balance.

The previous balance is in the file balance.txt. Recent transactions are in a file. The file contains one line per transaction. Every line contains a letter that explains what type of transaction it is and an amount.

Transaction codes: W for withdrawal, D for deposit.

After the transaction file is created, the new balance is placed in the file balance.txt.

Individual transactions that mean the account has a negative balance are all right. If the net result at the end is such that the balance is negative, however, the whole transaction file will be rejected. No transaction will be recorded in this case.

Example: Starting balance: $5,460.70. The transaction file has the following contents:

   W 450
   D 567.80
   D 4000
   W 500

Result: New balance is $9078.50.

Analysis

The Account class will store information about account number, holder name and balance. As services it will offer service to withdraw money and service to deposit money. As an extension point we will provide a private property for tracking the credit limit. At this time the class will provide unlimited credit by setting this property to Double.MAX_VALUE.

The application operates on a persistent instance of the Account class. In fact only the account balance is persisted. The account persistence is handled by an AccountStore class. The class offers two services: loadAccount and storeAccount.

Individual transactions are read and interpreted from a file by a TransactionReader class. The TransactionReader iterates over the file. The fetchTransaction method retrieves the next transaction from the file. getTransactionCode and getTransactionAmount methods can be used to obtain information about the last read transaction.

The transaction is executed by a TransactionProcessor class.

The above discussion can be illustrated by the following class diagram:

TODO: Add sequence diagram

Solution

We can start from the Account class.

/**
 * A simple class to represent bank account
 * @author Ivan Georgiev
 */
public class Account {
	private static int theNextNumber = 1;
	private final int theNumber;
	private String theName;
	private double theBalance;
	private double creditLimit = -Double.MAX_VALUE;
 
	public Account(String aName, double aBalance) {
		this(aName, aBalance, theNextNumber++);
	}
 
	public Account(String aName, double aBalance, int aNumber) {
		theName = aName;
		theBalance = aBalance;
		theNumber = aNumber;
	}
 
	public String getName() {
		return theName;
	}
 
	public double getBalance() {
		return theBalance;
	}
 
	public int getNumber() {
		return theNumber;
	}
 
	public boolean deposit(double amount) {
		boolean ok = false;
		if (amount > 0) {
			theBalance += amount;
			ok = true;
		}
		return ok;
	}
 
	public boolean withdraw(double amount) {
		boolean ok = false;
		if (amount > 0) {
			double newBalance = theBalance - amount;
			if (newBalance >= creditLimit) {
				theBalance = newBalance;
				ok = true;
			}
		}
		return ok;
	}
 
	public String toString() {
		return "Account #" + theNumber
			+ ", name: " + theName 
			+ ", balance: " + theBalance;
	}
}

The AccountStore class:

/**
 * Java class to implement account persistence for
 * the transaction proessor excercise problem.
 *
 * @author Ivan Georgiev
 */
import java.io.*;
 
public class AccountStore {
	private String storeFileName;
 
	public AccountStore(String fileName) {
		storeFileName = fileName;
	}
 
	public Account loadAccount(int number) 
		throws IOException
	{
		double balance;
		try {
			BufferedReader reader = new BufferedReader(new FileReader(storeFileName));
			balance = Double.parseDouble(reader.readLine());
			reader.close();
		} catch (FileNotFoundException fnfe) {
			balance = 0;
		} catch (NumberFormatException nfe) {
			balance = 0;
		}
		Account result = new Account("<Unknown>", balance, number);
		return result;
	}
 
 
	public void storeAccount(Account account)
		throws IOException
	{
		PrintWriter printer = new PrintWriter(
			new BufferedWriter(new FileWriter(storeFileName)));
		printer.println(account.getBalance());
		printer.close();
	}
}

The implementation for the TransactionReader class follows:

 
/**
 * A java class to interpret the transaction file in
 * transaction processor problem.
 *
 * @author Ivan Georgiev
 */
import java.io.*;
 
public class TransactionReader {
	private boolean isValid = false;
	private char theCode;
	private double theAmount;
	private BufferedReader theReader;
 
	public TransactionReader(String transactionFileName) 
		throws FileNotFoundException
	{
		theReader = new BufferedReader(new FileReader(transactionFileName));
	}
 
	public boolean fetchTransaction() {
		try {
			String sLine = theReader.readLine();
			if (sLine == null) {
				isValid = false;
			} else {
				java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(sLine);
				theCode = tokenizer.nextToken().charAt(0);
				theAmount = Double.parseDouble(tokenizer.nextToken());
				isValid = true;
			}			
		} catch (Exception e) {
			isValid = false;
		}
		return isValid;
	}
 
	public char getCode() {
		if (!isValid) throw new InvalidTransactionException();
		return theCode;
	}
 
	public double getAmount() {
		if (!isValid) throw new InvalidTransactionException();
		return theAmount;
	}
}

The InvalidTransactionException:

/**
 * InvalidTransactionException class for the transaction processor
 * excercise problem.
 * @author Ivan Georgiev
 */
public class InvalidTransactionException extends RuntimeException {
 
}

And finally the transaction processor code might look like the following:

/**
 * Transaction processor Java class for the 
 * transaction processsor excercise problem.
 *
 * @author Ivan Georgiev
 */
public class TransactionProcessor {
	private Account theAccount;
	private TransactionReader theTransaction;
 
	public TransactionProcessor(Account anAccount, TransactionReader aTransaction) {
		theAccount = anAccount;
		theTransaction = aTransaction;
	}
 
	public boolean execute() {
		boolean isOk = true;
		while (isOk && theTransaction.fetchTransaction()) {
			switch (theTransaction.getCode()) {
				case 'W':
					isOk = theAccount.withdraw(theTransaction.getAmount());
					break;
				case 'D':
					isOk = theAccount.deposit(theTransaction.getAmount());
					break;
				default:
					isOk = false;
					throw new InvalidTransactionException();
			}
		}
		return isOk;
	}
 
}

Test Application

The following application might be used to test and execute transaction file:

/**
 * TransactionProcessorApplication - application
 * to test and execute transactions in transaction processor application.
 *
 * @author Ivan Georgiev
 * @version 1.00 2008/9/7
 */
import java.io.*;
public class TransactionProcessorApplication {
	public static final String transactionFileName = "../data/transaction.txt"; 
	public static final String balanceFileName = "../data/balance.txt"; 
 
    public static void main(String[] args) {
    	try {
    		TransactionReader transaction = new TransactionReader(transactionFileName);
    		AccountStore store = new AccountStore(balanceFileName);
    		Account account = store.loadAccount(1); 
    		TransactionProcessor processor = new TransactionProcessor(account, transaction);
    		if (processor.execute()) {
    			if (account.getBalance() >= 0) {
    				store.storeAccount(account);
    				System.out.println("Transaction successful. New balance: " + account.getBalance());
    			} else {
    				System.out.println("Transaction failed (negative balance).");
    			}
    		} else {
    			System.out.println("Transaction failed");
    		}
    	} catch (InvalidTransactionException ite) {
    		System.out.println(ite);
    	} catch (FileNotFoundException fnfe) {
    		System.out.println(fnfe);
    	} catch (IOException ioe) {
    		System.out.println(ioe);
    	}
    }
}
 
java/excercises/accounttransactions.txt · Last modified: 2009/10/31 23:39 (external edit)
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki