simple design/programming nuggets

49
Simple Programming/Design Tidbits Banking application Customer, Accounts, Transactions, ATM,

Upload: vivek-singh

Post on 03-Jul-2015

715 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Simple design/programming nuggets

Simple Programming/Design Tidbits

Banking application

Customer, Accounts, Transactions, ATM,

Page 2: Simple design/programming nuggets

All transactions at an ATMclass AccountService {

List<Transaction> transactionsAt(int accountId, ATM atm) {List<Transaction> transactionList = new List<Transaction>();for (Transaction txn : transactions)

if (transaction.wasAtLocation(atm.getLocationId())) transactionList.add(txn);

return transactionList;}

}

class Transaction { ATM atm; boolean wasAtLocation(int locationId) { return atm.getLocationId() == locationId;

}}

Page 3: Simple design/programming nuggets

All transactions at an ATMAll transactions at an ATMclass AccountService {

List<Transaction> transactionsAt(int accountId, ATM atm) {List<Transaction> transactionList = new List<Transaction>();for (Transaction txn : transactions)

if (transaction.wasAt(atm))if (transaction.wasAt(atm)) transactionList.add(txn);

return transactionList;}

}

class Transaction {ATM atm;boolean wasAt(ATM atm) {boolean wasAt(ATM atm) {

return this.atm.equals(locationId);return this.atm.equals(locationId);}

}

Page 4: Simple design/programming nuggets

Pass object not its data

Encasulation can be broken across methods

Page 5: Simple design/programming nuggets

class Customer {static double eligibilityForHomeLoan = 500000;static double eligibilityForPersonalLoan = 10000;double salary;

boolean targetForHomeLoan() { return salary > eligibilityForHomeLoan; }

boolean targetForPersonalLoan() { return salary > eligibilityForPersonalLoan; }}

Should target a customer for loan?

Page 6: Simple design/programming nuggets

Should target a customer for loan?Should target a customer for loan?class Customer {

Salary salary;Salary salary; boolean targetForHomeLoan() { return salary.isEligibleForHomeLoan();return salary.isEligibleForHomeLoan(); }}class Salary {class Salary {

double amount;double amount;static double eligibilityForHomeLoan = 500000;static double eligibilityForHomeLoan = 500000;

Salary(double amount) {Salary(double amount) { this.amount = amount;this.amount = amount; }} boolean isEligibleForLoan() {boolean isEligibleForLoan() { return amount > eligibilityForHomeLoan.amount;return amount > eligibilityForHomeLoan.amount; }}}}

Page 7: Simple design/programming nuggets

Avoid primitives in your domain

Primitives not only language but can be domain primitives e.g. Money/Date/Currency

Page 8: Simple design/programming nuggets

class Customer { Set<Account> accounts;

Set<Account> getAccounts() { return accounts; }}

class CustomerService {void addAccount(Customer customer) {

Account account = new Account();customer.getAccounts().add(account);

}}

Open new account for a customer

Page 9: Simple design/programming nuggets

class Customer { Set<Account> accounts; Account[] getAccounts() {Account[] getAccounts() { return accounts.toArray();return accounts.toArray(); }}

void addNewAccount() {void addNewAccount() {Account account = new Account();Account account = new Account();accounts.add(account);accounts.add(account);

}}}class CustomerService {

void addAccount(Customer customer) {customer.addNewAccount();customer.addNewAccount();

}}

Open new account for a customerOpen new account for a customer

Page 10: Simple design/programming nuggets

Collections are mutable, donot expose them

Page 11: Simple design/programming nuggets

class Customer { Set<Account> accounts;

Money balance() {Money total = Money.Zero();

for (Account account : accounts) {total += account.getBalance();

}return total;

}

Money inactiveAccounts() { for (Account account : accounts) {

if (account.isInactive())inactiveAccounts.add(account);

} }}

Customer's accounts

Page 12: Simple design/programming nuggets

class Customer {Accounts accounts;Accounts accounts;

}class Accountsclass Accounts {

Set<Account> accounts;Set<Account> accounts;Money total = Money.Zero();Money balance() {

for (Account account : accounts) {total += account.getBalance();

}return total;

} Money inactiveAccounts() { for (Account account : accounts) {

if (account.isInactive())inactiveAccounts.add(account);

} }}

Customer's accounts

Page 13: Simple design/programming nuggets

Collections are primitives

Create domain specific collections

Page 14: Simple design/programming nuggets

class Account {InterestPlan interestPlan;Money bal;

Money applyInterest() { switch (interestPlan) {

case Simple: interest = new SICalc().calculate(bal);case Compound: interest = new CICalc().calculate(bal);......

}bal += interest;

}}

enum InterestPlan {Simple, Compound, InflationAdjusted, DurationDependent

}

Apply interest

Page 15: Simple design/programming nuggets

class Account {InterestPlan interestPlan;Money bal;static Map calculators = {static Map calculators = {

Simple => new SICalc(), Simple => new SICalc(), Compound => new CICalc();Compound => new CICalc();..........

}}

Money applyInterest() {interest = calculators[interestPlan].calculate();interest = calculators[interestPlan].calculate();

}}

Apply interestApply interest

Page 16: Simple design/programming nuggets

Externalize if conditions

Can use for functions as value in map as well

Page 17: Simple design/programming nuggets

class Account {State state;

boolean transactionsAllowed() { return state == State.Approved || state == State.Active; }

boolean onlineTransactionAllowed() {return state == State.Active || state == State.Locked;

}}

enum State {PendingApproval, Approved, Locked, Active, Closed

}

Account Security

Page 18: Simple design/programming nuggets

class Account {State state;List transactionsAllowed = {State.Approved, State.Active};List transactionsAllowed = {State.Approved, State.Active};List onlineTransactionsAllowed = {State.Locked, State.Active};List onlineTransactionsAllowed = {State.Locked, State.Active};

boolean transactionsAllowed() { return transactionsAllowed.contains(state)transactionsAllowed.contains(state); }

boolean onlineTransactionAllowed() { return onlineTransactionsAllowed.contains(state)onlineTransactionsAllowed.contains(state);

}}

enum State {PendingApproval, Approved, Locked, Active, Closed

}

Account SecurityAccount Security

Page 19: Simple design/programming nuggets

void monthlyReport() {TransactionRepository repo = new TransactionRepository(...);var list = repo.getBiggerTransactionsNotTransferredToSelf();......

}

class TransactionRepository { Transactions getBiggerTransactionsNotTransferredToSelf() {

Transactions transactions = loadTransactions(customerId);....logic...

}}

Suspicious Transactions

Page 20: Simple design/programming nuggets

void monthlyReport() {TransactionRepository repo = new TransactionRepository(...);var list = repo.suspiciousTransactions()repo.suspiciousTransactions();......

}

class TransactionRepository { Transactions suspiciousTransactions()suspiciousTransactions() {

Transactions transactions = loadTransactions(customerId);....logic...

}}

Suspicious TransactionsSuspicious Transactions

Page 21: Simple design/programming nuggets

Tell what not how

Keep implementation encapsulation

Seen more when doing TDD

Page 22: Simple design/programming nuggets

class Customer {String name;Date dateOfBirth;Accounts accounts;Money salaryForLoanCalculations;

boolean targetForHomeLoan() { return salaryForLoanCalculations.isEligibleForHomeLoan(); }

boolean targetForPersonalLoan() { return salaryForLoanCalculations.isEligibileForPersLoan(); }}

Customer

Page 23: Simple design/programming nuggets

class Customer {String name;Date dateOfBirth;Accounts accounts;Money salary;Money salary;

boolean targetForHomeLoan() { return salary.isEligibleForHomeLoan();return salary.isEligibleForHomeLoan(); }

boolean targetForPersonalLoan() { return salary.isEligibileForPersonalLoan();return salary.isEligibileForPersonalLoan(); }}

CustomerCustomer

Page 24: Simple design/programming nuggets

Store what not what for

Seen more when doing TDD

Page 25: Simple design/programming nuggets

class Customer {String name;Accounts accounts;Money salary;

boolean isLoyal() { if (salary > 20000 && accounts.balance() > 100000) {

return true;}return false;

}}

Is Loyal Customer?

Page 26: Simple design/programming nuggets

class Customer {String name;Accounts accounts;Money salary;

boolean isLoyal() { return salary > 20000 && accounts.balance() > 100000);return salary > 20000 && accounts.balance() > 100000); }}

Is Loyal Customer?Is Loyal Customer?

Page 27: Simple design/programming nuggets

class Customer {Accounts accounts;boolean isLoyal() {

....}

void issueCard(String accountNumber) {Account account = accounts.get(accountNumber);if (isLoyal() == true)

account.issuePlatinum();else

account.issueOfLastType();}

}

Issue platinum card?

Page 28: Simple design/programming nuggets

class Customer {Accounts accounts;boolean isLoyal() {

....}

void issueCard(String accountNumber) {Account account = accounts.get(accountNumber);if (isLoyal())if (isLoyal())

account.issuePlatinum();else

account.issueOfLastType();}

}

Issue platinum card?Issue platinum card?

Page 29: Simple design/programming nuggets

class Customer {String name;Date dateOfBirth;

public void validateForNew() throws ValidationException {ValidationException exception = new ValidationException();if (name == null || name.isEmpty())

exception.add(“Name not specified”);if (dateOfBirth == null)

exception.add(“Date of birth not specified”);else if (dateOfBirth.isBefore(1990))

exception.add(“below 20 years are not eligible”);if (exception.hasErrors()) {

throw exception;}

}}

New Customer

Page 30: Simple design/programming nuggets

class Customer {String name;Date dateOfBirth;

public ValidationError validateForNew() {public ValidationError validateForNew() {ValidationError error = new ValidationError();ValidationError error = new ValidationError();if (name == null || name.isEmpty())

error.add(“Name not specified”);if (dateOfBirth == null)

error.add(“Date of birth not specified”);else if (dateOfBirth.isBefore(1990))

error.add(“below 20 years are not eligible”);

return error;return error;}

}

New CustomerNew Customer

Page 31: Simple design/programming nuggets

Error during validation is not exceptional scenario

Page 32: Simple design/programming nuggets

public class Customer { public void gotMarried() { title = "Mrs"; }

public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;

Customer customer = (Customer) o;

if (!firstName.equals(customer.firstName)) return false; if (!lastName.equals(customer.lastName)) return false; if (!title.equals(customer.title)) return false;

return true; }

public int hashCode() { int result = firstName.hashCode(); result = 31 * result + lastName.hashCode(); result = 31 * result + title.hashCode(); return result; }}

Customer got married

Page 33: Simple design/programming nuggets

public void test() { Customer customer = new Customer("foo", "bar", "Miss"); Map map = new HashMap(); map.put(customer, "rich");

assertTrue(map.get(customer) != null); customer.gotMarried(); assertTrue(map.get(customer) != null); <-- FailsassertTrue(map.get(customer) != null); <-- Fails }

Customer got married

Page 34: Simple design/programming nuggets

Don't use mutable fields for hashcode

Page 35: Simple design/programming nuggets

Account account = AccountRepository.load(1);....cleanUp(account);

public static void cleanUp(Account account) { if (null != account) { deleteAccount(account, null); account = null; } }

Get rid of large object from memory

Page 36: Simple design/programming nuggets

Objects references are always passed by value

Thats why C# has explicit pass by ref (not recommending essentially)

Page 37: Simple design/programming nuggets

Class Customer {Name name;Date dateOfBirth;Accounts accounts;

Name getName() {if (name == null) name = new Name();return name;

}}

Get customer's name

Page 38: Simple design/programming nuggets

Do not change data in getter, it deceives

Page 39: Simple design/programming nuggets

ToString() is object's user interface

Reserve it and use for debugging only

Page 40: Simple design/programming nuggets

public class Customer { List<Account> accounts = new ArrayList<Account>();

public void isAccountInactive(String accountNumber, int maxInactivityAllowed) { int i = accounts.indexOf(new Account(accountNumber)); Account account = accounts.get(i); inactive(account, maxInactivityAllowed); }

private static boolean inactive(Account account, int maxInactivityAllowed) { return account.getLastActivity().within(maxInactivityAllowed); }}

What can we do here?

Page 41: Simple design/programming nuggets

public class Customer { List<Account> accounts = new ArrayList<Account>();

public void isAccountInactive(String accountNumber, int maxInactivityAllowed) { int i = accounts.indexOf(new Account(accountNumber)); Account account = accounts.get(i); account.inactive(maxInactivityAllowed);account.inactive(maxInactivityAllowed); }}

Look for staticsLook for statics

Page 42: Simple design/programming nuggets

Look out for static methods and hints (by IDE) to make methods static

Use the suggestion by not accepting it

Page 43: Simple design/programming nuggets

class Account {Transactions transactions;Customer customer;

boolean suggestLinkingOfAccounts() { if (transactions != null) { Transactions customerXAs = transactions.with(customer); return customerXAs.totalAmount() > 10000; } return false; }}

Suggest linking of accounts?

Page 44: Simple design/programming nuggets

class Account {Transactions transactions;Customer customer;

boolean suggestLinkingOfAccounts() { if (transactions != null) {

return transactions.withAndInExcessOf(customer, 10000);return transactions.withAndInExcessOf(customer, 10000); } return false; }}

Suggest linking of accounts?Suggest linking of accounts?

Page 45: Simple design/programming nuggets

Tell don't ask at second level(unless for performance reasons)

Page 46: Simple design/programming nuggets

class Customer {Transactions todaysTransactions;Accounts accounts;

public Customer(Accounts account, Date date) {this.accounts = accounts;todaysTransactions = accounts.TransactionDoneOn(date);

}

Transactions todaysTransaction() {Return todaysTransactions;

}}

Today's transactions

Page 47: Simple design/programming nuggets

class Customer {Accounts accounts;

public Customer(Accounts account)public Customer(Accounts account) {this.accounts = accounts;

}

Transactions allTransactionsOn(Date date) {Transactions allTransactionsOn(Date date) {Return accounts.TransactionDoneOn(date);Return accounts.TransactionDoneOn(date);

}}}

Today's transactions

Page 48: Simple design/programming nuggets

Model state based on domain

State shouldn't be based on object's usage, methods are for that

Page 49: Simple design/programming nuggets

Suggestions might not always apply