Source Code
AccountType.java
package com.greenpepper.confluence.demo.bank;
import java.util.EnumMap;
public enum AccountType
{
SAVINGS("savings", Money.$(200f), Money.$(200f), null, Money.$(1.5f), Money.$(2.5f), Money.$(0.5f)),
CHECKING("checking", Money.$(200f), Money.$(200f), null, Money.$(0), Money.$(2.50f), Money.$(0));
private final String description;
private EnumMap<WithdrawType, Money> limits;
private EnumMap<WithdrawType, Money> fees;
private AccountType(String description, Money limitBankMachine,
Money limitInteracMachine, Money limitPersonalCheck,
Money feesBankMachine, Money feesInteracMachine, Money feesPersonalCheck)
{
this.description = description;
limits = new EnumMap<WithdrawType, Money>(WithdrawType.class);
limits.put(WithdrawType.ATM, limitBankMachine);
limits.put(WithdrawType.INTERAC, limitInteracMachine);
limits.put(WithdrawType.PERSONAL_CHECK, limitPersonalCheck);
fees = new EnumMap<WithdrawType, Money>(WithdrawType.class);
fees.put(WithdrawType.ATM, feesBankMachine);
fees.put(WithdrawType.INTERAC, feesInteracMachine);
fees.put(WithdrawType.PERSONAL_CHECK, feesPersonalCheck);
}
@Override
public String toString()
{
return description;
}
public Money limitFor(WithdrawType withdrawType)
{
return limits.get(withdrawType);
}
public Money feesFor(WithdrawType withdrawType)
{
return fees.get(withdrawType);
}
public static boolean isNoLimit(Money limit)
{
return limit == null;
}
}
Bank.java
package com.greenpepper.confluence.demo.bank;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
public class Bank
{
private HashMap<String, BankAccount> accounts;
public Bank()
{
accounts = new HashMap<String, BankAccount>();
}
public boolean hasAccount(String accountNumber)
{
return accounts.containsKey(accountNumber);
}
public BankAccount getAccount(String accountNumber)
throws NoSuchAccountException
{
if (!hasAccount(accountNumber))
{
throw new NoSuchAccountException(accountNumber);
}
return accounts.get(accountNumber);
}
public SavingsAccount openSavingsAccount(String number, Owner owner)
{
if (hasAccount(number))
{
return null;
}
SavingsAccount account = new SavingsAccount(number, owner);
accounts.put(number, account);
return account;
}
public CheckingAccount openCheckingAccount(String number, Owner owner)
{
if (hasAccount(number))
{
return null;
}
CheckingAccount account = new CheckingAccount(number, owner);
accounts.put(number, account);
return account;
}
public Money deposit(Money amount, String number)
throws Exception
{
BankAccount account = accounts.get(number);
return account.deposit(amount);
}
public Money withdraw(Money amount, String number, WithdrawType type)
throws Exception
{
BankAccount account = accounts.get(number);
return account.withdraw(amount, type);
}
public void freezeAccount(String number)
{
BankAccount account = accounts.get(number);
account.freeze();
}
public Collection<BankAccount> getAccounts()
{
return Collections.unmodifiableCollection(accounts.values());
}
public void transfer(String numberFrom, String numberTo, Money amountToTransfert)
throws Exception
{
if (!hasAccount(numberFrom))
{
throw new NoSuchAccountException(numberFrom);
}
if (!hasAccount(numberTo))
{
throw new NoSuchAccountException(numberTo);
}
BankAccount accountFrom = accounts.get(numberFrom);
BankAccount accountTo = accounts.get(numberTo);
if (accountFrom.getOwner().getFirstName().equals(accountTo.getOwner().getFirstName())
&& accountFrom.getOwnerName().equals(accountTo.getOwnerName()))
{
accountFrom.withdraw(amountToTransfert);
accountTo.deposit(amountToTransfert);
}
else
{
throw new Exception("Can't transfer from not owned account !");
}
}
}
BankAccount.java
package com.greenpepper.confluence.demo.bank;
public abstract class BankAccount
{
private AccountType type;
private Money balance = Money.ZERO;
private String number;
private Owner owner;
public boolean frozen;
public BankAccount(AccountType accountType, String number, Owner owner)
{
this.number = number;
this.type = accountType;
this.owner = owner;
}
public abstract void checkFunds(Money amount)
throws Exception;
public Money withdraw(Money amount, WithdrawType withdrawType)
throws Exception
{
Money limit = type.limitFor(withdrawType);
if (!AccountType.isNoLimit(limit) && amount.strictlyGreaterThan(limit))
{
throw new Exception("Limit overpassed");
}
Money fees = type.feesFor(withdrawType);
return withdraw(amount.plus(fees));
}
public Money withdraw(Money amount)
throws Exception
{
checkNotFrozen();
checkFunds(amount);
balance = balance.minus(amount);
return balance;
}
private void checkNotFrozen()
throws Exception
{
if (frozen)
{
throw new Exception("Acccount frozen!");
}
}
public String getNumber()
{
return number;
}
public Money deposit(Money amount)
{
balance = balance.plus(amount);
return balance;
}
public Money getBalance()
{
return balance;
}
public boolean isFrozen()
{
return frozen;
}
public void freeze()
{
frozen = true;
}
public AccountType getType()
{
return type;
}
public Owner getOwner()
{
return owner;
}
public String getOwnerName()
{
return owner.getFullName();
}
}
BankFixture.java
package com.greenpepper.confluence.demo.bank;
import java.util.Collection;
public class BankFixture
{
private Bank bank;
public BankFixture()
{
this.bank = new Bank();
}
public boolean openSavingsAccountUnderTheNameOf(String number, String firstName, String lastName)
{
return bank.openSavingsAccount(number, new Owner(firstName, lastName)) != null;
}
public boolean openCheckingAccountUnderTheNameOf(String number, String firstName, String lastName)
{
return bank.openCheckingAccount(number, new Owner(firstName, lastName)) != null;
}
public boolean openAccountUnderTheNameOf(AccountType type, String number, String firstName, String lastName)
{
if (AccountType.SAVINGS == type)
{
return openSavingsAccountUnderTheNameOf(number, firstName, lastName);
}
else if (AccountType.CHECKING == type)
{
return openCheckingAccountUnderTheNameOf(number, firstName, lastName);
}
return false;
}
public Money thatBalanceOfAccountIs(String accountNumber)
throws Exception
{
BankAccount account = bank.getAccount(accountNumber);
return account.getBalance();
}
public boolean depositInAccount(Money amount, String accountNumber)
throws Exception
{
try
{
bank.deposit(amount, accountNumber);
return true;
}
catch (Exception e)
{
return false;
}
}
public boolean withdrawFromAccount(Money amount, String accountNumber)
throws Exception
{
return withdrawFromAccountUsing(amount, accountNumber, WithdrawType.ATM);
}
public boolean withdrawFromAccountUsing(Money amount, String accountNumber, WithdrawType withdrawType)
throws Exception
{
try
{
bank.withdraw(amount, accountNumber, withdrawType);
}
catch (Exception e)
{
return false;
}
return true;
}
public Collection getOpenedAccounts()
{
return bank.getAccounts();
}
public void freezeAccount(String accountNumber)
{
bank.freezeAccount(accountNumber);
}
public boolean createAccountForWithBalanceOf(AccountType type, String number, String firstName, String lastName,
Money balance)
throws Exception
{
BankAccount account = (type == AccountType.SAVINGS) ?
bank.openSavingsAccount(number, new Owner(firstName, lastName)) :
bank.openCheckingAccount(number, new Owner(firstName, lastName));
account.deposit(balance);
return true;
}
public boolean transferFromAccountToAccount(Money amountToTransfer, String fromAccountNumber,
String toAccountNumber)
throws Exception
{
try
{
bank.transfer(fromAccountNumber, toAccountNumber, amountToTransfer);
}
catch (Exception e)
{
return false;
}
return true;
}
}
CheckingAccount.java
package com.greenpepper.confluence.demo.bank;
public class CheckingAccount
extends BankAccount
{
private Money maxCredit = Money.ZERO;
public CheckingAccount(String number, Owner owner)
{
super(AccountType.CHECKING, number, owner);
}
public void checkFunds(Money amount)
throws Exception
{
if (getBalance().plus(maxCredit).lowerThan(amount))
{
throw new Exception("Not enougth credit !");
}
}
public void setCreditLine(Money credit)
{
this.maxCredit = credit;
}
public Money limitFor(WithdrawType type)
{
return AccountType.CHECKING.limitFor(type);
}
}
Money.java
package com.greenpepper.confluence.demo.bank;
import java.math.BigDecimal;
public class Money
{
public static final Money ZERO = new Money(BigDecimal.ZERO);
private final BigDecimal dollars;
public Money(BigDecimal dollars)
{
this.dollars = dollars;
}
public static Money parse(String text)
{
return new Money(new BigDecimal(normalize(text)));
}
private static String normalize(String text)
{
return text.replaceAll("\\$", "").replaceAll(",", "").replaceAll("\\s", "");
}
public static Money dollars(float amount)
{
return new Money(new BigDecimal(amount));
}
public static Money $(float amount)
{
return dollars(amount);
}
public static Money zero()
{
return dollars(0f);
}
public boolean equals(Object other)
{
if (other instanceof Money)
{
Money that = (Money)other;
return this.dollars.compareTo(that.dollars) == 0;
}
else
{
return false;
}
}
public int hashCode()
{
return dollars.hashCode();
}
public String toString()
{
return "$" + dollars;
}
public Money times(BigDecimal multiplier)
{
return new Money(dollars.multiply(multiplier));
}
public Money times(float multiplier)
{
return new Money(dollars.multiply(new BigDecimal(multiplier)));
}
public Money minus(Money subtrahend)
{
return new Money(dollars.subtract(subtrahend.dollars));
}
public Money plus(Money addend)
{
return new Money(dollars.add(addend.dollars));
}
public boolean greaterThan(Money money)
{
return this.dollars.compareTo(money.dollars) >= 0;
}
public boolean strictlyGreaterThan(Money money)
{
return this.dollars.compareTo(money.dollars) > 0;
}
public boolean lowerThan(Money money)
{
return dollars.compareTo(money.dollars) <= 0;
}
public boolean strictlyLowerThan(Money money)
{
return dollars.compareTo(money.dollars) < 0;
}
}
NoSuchAccountException.java
package com.greenpepper.confluence.demo.bank;
public class NoSuchAccountException
extends Exception
{
private final String number;
public NoSuchAccountException(String number)
{
this.number = number;
}
public String getNumber()
{
return number;
}
public String getMessage()
{
return "Account does not exist: " + number;
}
}
Owner.java
package com.greenpepper.confluence.demo.bank;
public class Owner
{
private final String firstName;
private final String lastName;
public Owner(String firstName, String lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName()
{
return firstName;
}
public String getLastName()
{
return lastName;
}
public String getFullName()
{
return firstName + " " + lastName;
}
}
SavingsAccount.java
package com.greenpepper.confluence.demo.bank;
public class SavingsAccount
extends BankAccount
{
public SavingsAccount(String number, Owner owner)
{
super(AccountType.SAVINGS, number, owner);
}
public void checkFunds(Money amount)
throws Exception
{
if (getBalance().lowerThan(amount))
{
throw new Exception("Not enougth money !");
}
}
}
WithdrawType.java
package com.greenpepper.confluence.demo.bank;
public enum WithdrawType
{
ATM("ATM"),
INTERAC("Interact"),
PERSONAL_CHECK("Personal Check");
private final String description;
private WithdrawType(String description)
{
this.description = description;
}
@Override
public String toString()
{
return description;
}
}