#!/usr/bin/env fan mixin Actor { abstract Role actAs(Type role) } mixin Role { abstract Role apply(Actor actor) } class Account : Actor { readonly Decimal balance new make(Decimal balance := 0d) { this.balance = balance } override Role actAs(Type role) { try { return ((Role)role.make).apply(this) } catch (Err e) { throw Err("illegal argument", e) } } Void deposit(Decimal amount) { balance += amount } Void withdraw(Decimal amount) { balance -= amount } } mixin MoneySource : Role { abstract Void withdraw(Decimal amount) } mixin MoneySink : Role { abstract Void deposit(Decimal amount) } class TransferMoneySource : MoneySource { Account? account new make(Actor? actor := null) { account = actor } override Role apply(Actor actor) { return TransferMoneySource(actor) } Void transfer(Decimal amount, MoneySink to) { withdraw(amount) to.deposit(amount) } override Void withdraw(Decimal amount) { account?.withdraw(amount) } } class TransferMoneySink : MoneySink { Account? account new make(Actor? actor := null) { account = actor } override Role apply(Actor actor) { return TransferMoneySink(actor) } Void transfer(Decimal amount, MoneySource from) { from.withdraw(amount) deposit(amount) } override Void deposit(Decimal amount) { account?.deposit(amount) } } mixin UseCase { abstract Void execute() } class WithDrawal : UseCase { Account savingsAccount Account investmentAccount Decimal amount new make(Account savingsAccount, Account investmentAccount, Decimal amount) { this.savingsAccount = savingsAccount this.investmentAccount = investmentAccount this.amount = amount } override Void execute() { source := (TransferMoneySource) savingsAccount.actAs(TransferMoneySource#) sink := (TransferMoneySink) investmentAccount.actAs(TransferMoneySink#) source.transfer(amount, sink) } } class Deposit : UseCase { Account savingsAccount Account investmentAccount Decimal amount new make(Account savingsAccount, Account investmentAccount, Decimal amount) { this.savingsAccount = savingsAccount this.investmentAccount = investmentAccount this.amount = amount } override Void execute() { source := (TransferMoneySource) investmentAccount.actAs(TransferMoneySource#) sink := (TransferMoneySink) savingsAccount.actAs(TransferMoneySink#) source.transfer(amount, sink) } } class Main { static Void main() { srcAccount := Account(100d) dstAccount := Account(200d) echo("srcAccount.balance=${srcAccount.balance}") echo("dstAccount.balance=${dstAccount.balance}") WithDrawal(srcAccount, dstAccount, 50d).execute echo("----------------------------------------") echo("srcAccount.balance=${srcAccount.balance}") echo("dstAccount.balance=${dstAccount.balance}") Deposit(srcAccount, dstAccount, 100d).execute echo("----------------------------------------") echo("srcAccount.balance=${srcAccount.balance}") echo("dstAccount.balance=${dstAccount.balance}") } }