,

projection: Context-Classes

Class: BB5aMoneyTransferCTX

BB1Context subclass: #BB5aMoneyTransferCTX
    instanceVariableNames: 'transaction'
    category: 'BB5aBank-Context'

" This is a very simple example where a user operates an automatic teller machine to transfer money from one bank account to another.
Bankers know that accounts actually exist as a summation of transaction records in the General Ledger transction collection. The accounts in this example are to be understood as caches on the Ledger.
Version a (this version) ignores the Ledger for the sake of simplicity.
Version b is more correct in that includes the Ledger with its transaction records. "

instanceMethods: role binding

AMOUNT
    ^transaction amount

DESTINATIONACCOUNT
    ^ transaction toAccount

SOURCEACCOUNT
    ^transaction fromAccount

instanceMethods: system operations

transferTransaction: transact
    transaction := transact.
    ^ self triggerInteractionFrom: #SOURCEACCOUNT with: #transferTo.

projection: BB5aMoneyTransferCTX-Interaction

No diagram

roleMethods: DESTINATIONACCOUNT

transferFrom
    self increase: AMOUNT.
    ^true

roleMethods: SOURCEACCOUNT

transferTo
    SOURCEACCOUNT balance >= AMOUNT
        ifTrue:
            [self decrease: AMOUNT.
            ^DESTINATIONACCOUNT transferFrom]
        ifFalse:
            [self inform: 'Error. Insufficient funds'.
            ^false]


projection: Data-classes

Class: BB5aAccount

Object subclass: #BB5aAccount
    instanceVariableNames: 'balance'
    category: 'BB5aBank-Data'

" An Account object is a cache on the Ledger transactions. "

instanceMethods: initialize-release

initialize
    balance := 0.

instanceMethods: attributes-read

balance
    ^balance

instanceMethods: operations

increase: amount
    balance := balance + amount.

decrease: amount
    balance := balance - amount.

Class: BB5aBank

Object subclass: #BB5aBank
    instanceVariableNames: 'transactions accounts bank'
    category: 'BB5aBank-Data'

instanceMethods: initialize-release

initialize
    super initialize.
    accounts := Dictionary new.
    transactions := OrderedCollection new.

instanceMethods: attributes-read

findAccountNo: accountNumber
    ^accounts at: accountNumber ifAbsent: [nil]

transactions
    ^transactions

instanceMethods: operations

addAccountNo: aNumber
    ^accounts at: aNumber put: BB5aAccount new.

addTransaction: trans
    transactions add: trans.

Class: BB5aTransaction

Object subclass: #BB5aTransaction
    instanceVariableNames: 'bank date amount fromAccNo toAccNo'
    category: 'BB5aBank-Data'

instanceMethods: initialize

bank: bnk amount: amt from: from to: to
    bank := bnk.
    date := Time dateAndTimeNow.
    amount := amt.
    fromAccNo := from.
    toAccNo := to.

instanceMethods: accessing

amount
    ^ amount

bank
    ^ bank

date
    ^ date

fromAccount
    ^bank findAccountNo: fromAccNo

toAccount
    ^bank findAccountNo: toAccNo

projection: Testing-classes

Class: BB5aTesting

Object subclass: #BB5aTesting
    instanceVariableNames: 'bank account1 account2'
    category: 'BB5aBank-Testing'

" See comment in Context >> BB5aMoneyTransferContext "

instanceMethods: operations

test
    " BB5aTesting test "
    bank := BB5aBank new.
    account1 := bank addAccountNo: 1111.
    account2 := bank addAccountNo: 2222.
    self test1.
    self test2.

test1
    | transaction1 |
    account1 increase: 2000.
    self assert:
            [account1 balance = 2000 &
            account2 balance = 0].
    transaction1 := BB5aTransaction new bank: bank amount: 500 from: 1111 to: 2222.
    (BB5aMoneyTransferCTX new transferTransaction: transaction1)
    ifTrue:
        [self assert:
                [(bank findAccountNo: 1111) balance = 1500 &
                (bank findAccountNo: 2222) balance = 500].    
        self inform: 'Test1 OK' , String cr , 'Close this box and get error: Insuffucient funds.'.]
    ifFalse:
        [self inform: 'Test1 failure'].

test2
    | transaction2 |
    self assert:
            [account1 balance = 1500 &
            account2 balance = 500].
    transaction2 := BB5aTransaction new bank: bank amount: 5000 from: 1111 to: 2222.
    (BB5aMoneyTransferCTX new transferTransaction: transaction2)
    ifTrue:
        [self assert:
                [(bank transactions size > 0 and: [bank transactions last == transaction2])&
                (bank findAccountNo: 1111) balance = 1000 &
                (bank findAccountNo: 2222) balance = 1000]
        descriptionBlock: [self inform: 'Test2 OK, it has failed as expected!']]    
    ifFalse:
        [self inform: 'Test2 OK, it has failed as expected!'].

classMethods: tests

test
    " BB5aTesting test "
    self new test.



Top
Context-Classes
BB5aMoneyTransferCTX
Interaction
Data-classes
BB5aAccount
BB5aBank
BB5aTransaction
Testing-classes
BB5aTesting