Skip to content

Instantly share code, notes, and snippets.

@mbrowne
Last active August 22, 2023 16:43
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mbrowne/36d17f932d0c61f0b575526c39398ce8 to your computer and use it in GitHub Desktop.
Save mbrowne/36d17f932d0c61f0b575526c39398ce8 to your computer and use it in GitHub Desktop.
DCI in Swift - simple money transfer example
//TransferMoney context
class TransferMoney: Context {
private let SourceAccount: SourceAccountRole
private let DestinationAccount: DestinationAccountRole
init(source:AccountData, destination:AccountData) {
SourceAccount = source as! SourceAccountRole
DestinationAccount = destination as! DestinationAccountRole
}
func transfer(amount:Int) {
SourceAccount.transferFrom(amount)
}
//note that we cast to base Role protocol so roles methods aren't accessible
//outside the Context by default
func getDestinationAccount() -> Role {
return DestinationAccount
}
}
protocol SourceAccountRole: Role {
func transferFrom(amt: Int)
//requires:
func decreaseBalance(amt: Int)
}
extension SourceAccountRole {
func transferFrom(amt: Int) {
decreaseBalance(amt)
DestinationAccount.transferTo(amt)
}
private var DestinationAccount: DestinationAccountRole {
get {
return (Context.currentContext as! TransferMoney).getDestinationAccount() as! DestinationAccountRole
}
}
}
protocol DestinationAccountRole: Role {
func transferTo(amt: Int)
//requires:
func increaseBalance(amt: Int)
}
extension DestinationAccountRole {
func transferTo(amt: Int) {
increaseBalance(amt)
}
}
//end TransferMoney context
///////////////////////////
protocol AccountData {
var balance: Int {get}
func increaseBalance(amt: Int)
func decreaseBalance(amt: Int)
}
class Account: AccountData, SourceAccountRole, DestinationAccountRole {
private var _balance = 0
var balance: Int {
get {return _balance}
}
func increaseBalance(amt: Int) {
_balance += amt
}
func decreaseBalance(amt: Int) {
_balance -= amt
}
//Hide the constructor and use a factory method instead so that
//new instances are typed as AccountData rather than Account by default.
//(The Account type would be able to access role methods from outside the Context.)
private init() {}
class func create() -> AccountData {
return Account()
}
}
//DCI library classes
protocol Role {}
class Context {
private static var _currentContext: Context?
private var _parentContext: Context?
static var currentContext: Context? {
get {return _currentContext}
}
init() {
_parentContext = Context._currentContext
Context._currentContext = self
}
deinit {
Context._currentContext = _parentContext
}
}
//Run the use case
let src = Account.create()
src.increaseBalance(30)
let dst = Account.create()
dst.increaseBalance(30)
TransferMoney(source:src, destination:dst).transfer(10)
print("Source balance: ", src.balance)
print("Destination balance: ", dst.balance)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment