I recently took a programming exam for an interview and unfortunately didn't pass it. I wasn't offered any feedback about the results. I'm looking for help on how to improve my OOP skills.
The requirements for the program were:
- a
bank
has aname
- a
bank
has manyaccounts
transactions
are stored on theaccounts
.- There are different types of
accounts
:savings
andchecking
.Checking accounts
can have multiple types,money market
andindividual
.Individual accounts
can't withdraw more than $1000 at a time.- I need to demonstrate withdrawal, deposit, and transferring funds through unit tests.
The code currently compiles and the unit tests run successfully. Here is the main program:
Bank.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace BankExcercise
{
public class Bank
{
public string name;
private List<Account> bankAccounts;
public Bank(string name)
{
this.name = name;
bankAccounts = new List<Account>();
}
public int OpenBankAccount(Type accountType, decimal startingBalance)
{
int newId = bankAccounts.Count();
bankAccounts.Add((Account)Activator.CreateInstance(accountType, newId, startingBalance));
return newId;
}
public Account GetAccount(int ownerId)
{
Account account = bankAccounts.Where(x => x.owner == ownerId).FirstOrDefault();
if(account == null)
{
throw new ApplicationException("no account exists with that id");
}
return account;
}
public bool TransferFunds(int fromAccountId, int toAccountId, decimal transferAmount)
{
if(transferAmount <= 0)
{
throw new ApplicationException("transfer amount must be positive");
}
else if (transferAmount == 0)
{
throw new ApplicationException("invalid transfer amount");
}
Account fromAccount = GetAccount(fromAccountId);
Account toAccount = GetAccount(toAccountId);
if(fromAccount.balance < transferAmount)
{
throw new ApplicationException("insufficient funds");
}
fromAccount.Transfer(-1 * transferAmount, toAccountId);
toAccount.Transfer(transferAmount, fromAccountId);
return true;
}
}
}
I decided to abstract an Account
from which savings
and checking
would derive.
Account.cs
using BankExcercise.Transactions;
using System;
using System.Collections.Generic;
namespace BankExcercise
{
public abstract class Account
{
public int owner { get; set; }
public decimal balance { get; set; }
public List<Transaction> transactions { get; set; }
public Account(int owner, decimal balance)
{
this.owner = owner;
this.balance = balance;
transactions = new List<Transaction>();
}
public virtual bool Withdrawl(decimal withdrawlAmount)
{
if (balance < withdrawlAmount)
{
throw new ApplicationException("insufficient funds");
}
else if (withdrawlAmount <= 0)
{
throw new ApplicationException("invalid withdrawl amount");
}
balance -= withdrawlAmount;
Transaction newTransaction = new WithdrawlTransaction(withdrawlAmount);
transactions.Add(newTransaction);
return true;
}
public void Transfer(decimal transferAmount, int transferToId)
{
balance += transferAmount;
TransferTransaction newTransaction = new TransferTransaction(transferAmount, transferToId, owner);
transactions.Add(newTransaction);
}
public void Deposit(decimal amount)
{
if (amount <= 0)
{
throw new ApplicationException("invalid deposit amount");
}
balance += amount;
Transaction newTransaction = new DepositTransaction(amount);
transactions.Add(newTransaction);
}
}
}
SavingsAccount.cs
namespace BankExcercise.Accounts
{
public class SavingsAccount : Account
{
public SavingsAccount(int id, decimal newBalance) : base(id, newBalance) { }
}
}
I created a CheckingAccount
abstract class that would inherit from Account
CheckingAccount.cs
namespace BankExcercise.Accounts
{
public abstract class CheckingAccount : Account
{
public CheckingAccount(int owner, decimal balance) : base(owner, balance)
{
}
}
}
IndividualAccount
and MoneyMarket
would inherit from CheckingAccount
IndividualAccount.cs
using BankExcercise.Transactions;
using System;
namespace BankExcercise.Accounts.Checking
{
public class IndividualAccount : CheckingAccount
{
public IndividualAccount(int owner, decimal balance) : base(owner, balance)
{
}
public override bool Withdrawl(decimal withdrawlAmount)
{
if (balance < withdrawlAmount)
{
throw new ApplicationException("insufficient funds");
}
else if(withdrawlAmount > 1000)
{
throw new ApplicationException("withdrawl limit exceeded");
}
else if (withdrawlAmount <= 0)
{
throw new ApplicationException("invalid withdrawl amount");
}
balance -= withdrawlAmount;
Transaction newTransaction = new WithdrawlTransaction(withdrawlAmount);
transactions.Add(newTransaction);
return true;
}
}
}
There were no explicit instructions to follow for MoneyMarket
MoneyMarket.cs
namespace BankExcercise.Accounts.Checking
{
public class MoneyMarketAccount : CheckingAccount
{
public MoneyMarketAccount(int owner, decimal balance) : base(owner, balance)
{
}
}
}
Unit Tests
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BankExcercise;
using BankExcercise.Accounts.Checking;
namespace BankExcerciseTests
{
[TestClass]
public class IndividualAccountTests
{
public Bank bank;
public Account bankAccount;
public int accountId;
[TestInitialize]
public void AccountSetup()
{
bank = new Bank("USA BANK");
accountId = bank.OpenBankAccount(typeof(IndividualAccount), 10000);
bankAccount = bank.GetAccount(accountId);
}
[TestMethod]
public void AccountIdShouldMatchGivenValue()
{
Assert.AreEqual(bankAccount.owner, accountId);
}
[TestMethod]
public void CheckingBalanceShouldMatchAccountBalance()
{
Assert.AreEqual(bankAccount.balance, 10000);
}
[TestMethod]
public void DepositingShouldSucceed()
{
bank.GetAccount(accountId).Deposit(100);
Assert.AreEqual(bankAccount.balance, 10100);
}
[TestMethod]
public void WithdrawingShouldSucceed()
{
Assert.IsTrue(bankAccount.Withdrawl(999));
Assert.AreEqual(bankAccount.balance, 9001);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void WithdrawlingMoreThanLimitShouldFail()
{
bankAccount.Withdrawl(1001);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void OverdraftingShouldFail()
{
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
Assert.IsTrue(bankAccount.Withdrawl(1000));
bankAccount.Withdrawl(1000);
}
}
}
Unit Tests
using BankExcercise;
using BankExcercise.Accounts;
using BankExcercise.Accounts.Checking;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace BankExcerciseTests
{
[TestClass]
public class TransactionTests
{
public Bank bank;
[TestInitialize]
public void BankSetup()
{
bank = new Bank("First Bank of America");
bank.OpenBankAccount(typeof(IndividualAccount), 1465181);
bank.OpenBankAccount(typeof(SavingsAccount), 100);
bank.OpenBankAccount(typeof(MoneyMarketAccount), 15000);
bank.OpenBankAccount(typeof(SavingsAccount), 2564);
bank.OpenBankAccount(typeof(IndividualAccount), 87945);
bank.OpenBankAccount(typeof(SavingsAccount), 1000000001);
}
[TestMethod]
public void TransferringFromAnAccountWithCorrectBalanceShouldSucceed()
{
Assert.IsTrue(bank.TransferFunds(0, 2, 1465181));
Assert.AreEqual(bank.GetAccount(0).balance, 0);
Assert.AreEqual(bank.GetAccount(2).balance, 1480181);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void TransferringFromAnAccountWithInsufficientFundsShouldFail()
{
bank.TransferFunds(4, 3, 88947);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void TransferringWithInvalidToAccountIdShouldFail()
{
bank.TransferFunds(1, -1, 100);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void TransferringWithInvalidFromAccountIdShouldFail()
{
bank.TransferFunds(1000, 4, 4658);
}
[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void TransferringInvalidAmountShouldFail()
{
bank.TransferFunds(1, 2, -594);
}
}
}