Sunday, December 27, 2009

Design Patterns - WWW (What/ When / Why)

  • What is a design pattern? - Someone has already solved your problem - (Head First Design Pattern).
  • When should we use it? - Depend upon what kind of problem you want to solve .. there might be already a mature solution (in design) for the problem.. so that means you need to spend some time understanding existing design patterns and where you should apply them.. if you think none of the existing design pattern suits to your problem then you can share your experience with the community and you never know .. might be the next design pattern belongs to you..
  • Why should we use it? As already said - Someone has already solved your problem.. and when you apply it your code it make self explanatory . so that if some one else is looking to your code can easily make what kind of problem you are trying to solve.. ah you got free documentation...

Source code for ATM Client

1.
package com.cp.exercise.atm;

import java.util.HashMap;
import java.util.Map;

import com.cp.exercise.atm.exception.InsufficientBalanceException;
import com.cp.exercise.atm.exception.InvalidAccountExcetion;
import com.cp.exercise.atm.exception.InvalidDenominationException;
import com.cp.exercise.atm.exception.InvalidUserAccountExcetion;

public class ATMClient {

private static ATMClient client;

private ATMClient() {
}

public static ATMClient instanceOf() {
if(client == null) {
client = new ATMClient();
} return client;
}

private final Map userToAvailableBalance = new HashMap();

public synchronized void filledUserAccountWithInitialBalance(UserAccount userAccount, int initialBalance) {
validateAccount(userAccount);
userToAvailableBalance.put(userAccount, initialBalance);
}

public synchronized Integer checkBalance(UserAccount userAccount) {
return getBalance(userAccount);
}

public synchronized Map withdrawThisMuchAmount(UserAccount userAccount, int withDrawAmount) {
userHaveEnoughMoney(userAccount, withDrawAmount);
denominationIsCorrect(withDrawAmount);
reAdjustAccountBalance(userAccount, withDrawAmount);
return getTotalNotesForThisAmount(withDrawAmount);
}

private Integer getBalance(UserAccount userAccount) {
if(userToAvailableBalance.containsKey(userAccount)) {
return userToAvailableBalance.get(userAccount);
}
throw new InvalidUserAccountExcetion(String.format("Account [%s] does not exist.", userAccount));
}

private void validateAccount(UserAccount userAccount) {
if(!userAccount.isValidAccount()) {
throw new InvalidAccountExcetion(String.format("Account [%s] is not valid", userAccount));
}
}

private void reAdjustAccountBalance(UserAccount userAccount, int withDrawAmount) {
final int balance = getBalance(userAccount) - withDrawAmount;
userToAvailableBalance.put(userAccount, balance);
}

private Map getTotalNotesForThisAmount(int withDrawAmount) {
return Denomination.totalNoOfNotes(withDrawAmount);
}

private void denominationIsCorrect(int withDrawAmount) {
if(!Denomination.isCorrect(withDrawAmount)) {
throw new InvalidDenominationException(String.format("This amount [%s] is not valid. Please enter multilier of [%s]", withDrawAmount,Denomination.names()));
}
}

private void userHaveEnoughMoney(UserAccount userAccount, int withDrawAmount) {
if(getBalance(userAccount) <> getBalance(userAccount)) {
throw new InsufficientBalanceException(String.format("Account [%s] has insufficient balance [%s].Requested amount [%s]",
userAccount, getBalance(userAccount), withDrawAmount));
}
}
}

Test case for ATM Client

package com.cp.exercise.atm;

import java.util.Map;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.cp.exercise.atm.exception.InsufficientBalanceException;
import com.cp.exercise.atm.exception.InvalidDenominationException;
import com.cp.exercise.atm.exception.InvalidUserAccountExcetion;

public class TestATMClient {

private ATMClient client;
private UserAccount userAccount;

@Before
public void filledUserAccountWithBalance() {
client = ATMClient.instanceOf();
userAccount = new UserAccount("123455632", "pin");
final int tenThousand = Denomination.FIVE_HUNDRED.getNote() * 20;
client.filledUserAccountWithInitialBalance(userAccount, tenThousand);
}

@Test(expected=InvalidUserAccountExcetion.class)
public void shouldThrowAnExceptionIfAccountDoesNotExist() {
final UserAccount invalidUserAccount = new UserAccount("", "");
Assert.assertEquals(10000, client.checkBalance(invalidUserAccount));
}

@Test
public void userShouldAbleSeeTotalAvailableBalance() {
Assert.assertEquals(10000, client.checkBalance(userAccount));
}

@Test
public void userShouldAbleToWithdrawAmountWithCorrectDenomination() {
final Map denominationToCount = client.withdrawThisMuchAmount(userAccount, 2200);

Assert.assertEquals(4, denominationToCount.get(Denomination.FIVE_HUNDRED));
Assert.assertEquals(2, denominationToCount.get(Denomination.HUNDRED));
Assert.assertEquals(null, denominationToCount.get(Denomination.FIFTY));
Assert.assertEquals(7800, client.checkBalance(userAccount));
}

@Test(expected=InvalidDenominationException.class)
public void userShouldGetAnErrorMessageIfTheWithdrawelAmountIsNotMultilierOfAvailableDenomination() {
client.withdrawThisMuchAmount(userAccount, 1999);
}

@Test(expected=InsufficientBalanceException.class)
public void userShouldGetAnErrorMessageIfMinimumBalanceFallBelowMinimumDenomination() {
client.withdrawThisMuchAmount(userAccount, 11000);
}
}

Some problems and solution using - TDD

1. ATM Client -
Today I was thinking of writing a sample program which can be used as an ATM client.

Problem - Write a simple java program which act as an ATM client and do following :-
a) User should able to populate their account with some initial amount .. sound funny? latter one we will change this. so that only admin can do that
b) Support for 50, 100 and 500 denomination.
c) Error handling for invalid account, denomination, over flow requested amount.
d) User should able to check account balance.
e) User should able to withdraw valid amount.. in return ATM machine should return the total no of notes for each denomination.

Ok.. enough writing about the problem .. Let us write some code.. code?
or test cases .. I vote for test cases..

What all you need -
1. JDK 5
2. Eclipse
3. Junit 4