Skip to content

Instantly share code, notes, and snippets.

@eofster
Last active April 24, 2016 12:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save eofster/62e571d3dd87664935ed to your computer and use it in GitHub Desktop.
Save eofster/62e571d3dd87664935ed to your computer and use it in GitHub Desktop.
Vending machine vend() function refactored
func vend(itemNamed name: String) throws {
let item = try validatedItemNamed(name)
reduceDepositedCoinsBy(item.price)
removeFromInventory(item, name: name)
dispense(name)
}
private func validatedItemNamed(name: String) throws -> Item {
let item = try itemNamed(name)
try validate(item)
return item
}
private func reduceDepositedCoinsBy(price: Int) {
coinsDeposited -= price
}
private func removeFromInventory(item: Item, name: String) {
var item = item
item.count -= 1
inventory[name] = item
}
private func itemNamed(name: String) throws -> Item {
if let item = inventory[name] {
return item
} else {
throw VendingMachineError.InvalidSelection
}
}
private func validate(item: Item) throws {
try validateCount(item.count)
try validatePrice(item.price)
}
private func validateCount(count: Int) throws {
if count == 0 {
throw VendingMachineError.OutOfStock
}
}
private func validatePrice(price: Int) throws {
if coinsDeposited < price {
throw VendingMachineError.InsufficientFunds(coinsNeeded: price - coinsDeposited)
}
}
@cwagdev
Copy link

cwagdev commented Jan 1, 2016

I love the sentiment here, but why not use guard in your validate methods? I feel it reads a little cleaner as you get to write your condition to what you actually want, rather than thinking of the inverse and writing what you don't want.

func vend(itemNamed name: String) throws {
  let item = try validatedItemNamed(name)
  reduceDepositedCoinsBy(item.price)
  removeFromInventory(item, name: name)
  dispense(name)
}

private func validatedItemNamed(name: String) throws -> Item {
  let item = try itemNamed(name)
  try validate(item)
  return item
}

private func reduceDepositedCoinsBy(price: Int) {
  coinsDeposited -= price
}

private func removeFromInventory(var item: Item, name: String) {
  --item.count
  inventory[name] = item
}

private func itemNamed(name: String) throws -> Item {
  guard let item = inventory[name] else {
    throw VendingMachineError.InvalidSelection
  }

  return item
}

private func validate(item: Item) throws {
  try validateCount(item.count)
  try validatePrice(item.price)
}

private func validateCount(count: Int) throws {
  guard count > 0 else {
    throw VendingMachineError.OutOfStock
  }
}

private func validatePrice(price: Int) throws {
  guard coinsDeposited > price else {
    throw VendingMachineError.InsufficientFunds(coinsNeeded: price - coinsDeposited)
  }
}

@eofster
Copy link
Author

eofster commented Apr 24, 2016

@cwagdev, I agree it reads better with guard!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment