Vending Machine Slides
P | N
- Designing a Vending Machine
- User request:
- Build me a new vending machine
- A machine with columns of cans in it. Put in coins. Make a selection. Get a Jolt.
- Simulate to test, reuse the code in the real thing.
- Keys: Flexibility, Reuse
- DON'T GO ANY FURTHER!
- If you're printing these, don't look at the next page yet.
- Coad and Nicola's OOA for the Vending Machine

- (Feel free to critize mightily)
- Doing OOA/OOD/OOP in two pieces
- First, Dispensing Holder and Item
- Second, CashDevice
- A Scenario Script for DispensingHolder and Item
- I'm an item.
- Someone tells me to vend myself.
- I need some help getting enough cash.
- I tell my dispensing holder to dispense.
- I'm a dispensing holder.
- Someone tells me to dispense.
- I look to see if the item that I'm told to dispense is dispensable. If it is, then I dispense it.
- I return a result to the sender - "dispensed" or "not dispensed."
- I'm an item.
- If my dispensing holder returns "dispensed," then I delete myself.
- I need some help making change.
- I return a "dispensed" or "not dispensed" result to the sender.
- Defining Item
- Model subclass: #Item
- instanceVariableNames: 'name price dispensingHolder '
- classVariableNames: ''
- poolDictionaries: ''
- category: 'Vending'
- !Item class methodsFor: 'instance creation'!
- new
- !Item methodsFor: 'initialize-release'!
- initialize
- name := String new.
- price := 0.0
- Item's Accessors
- !Item methodsFor: 'accessing'!
- name
- name: aName
- "Set the value of name. Notify my dependents
- that I've changed"
- name := aName.
- self changed: #name
- price
- "Return my price "
- ^price
- price: aPrice
- "Set the value of price. Notify my dependents that
- price := aPrice.
- self changed: #price
- Connecting and Vending in Item
- !Item methodsFor: 'connections'!
- connectToDispensingHolder: aDispensingHolder
- dispensingHolder := aDispensingHolder
- dispensingHolder
- !Item methodsFor: 'vending'!
- vend
- ^dispensingHolder dispense: self
- Another way to patch printString
- !Item methodsFor: 'printing'!
- printOn: aStream
- aStream nextPutAll: 'item: '; space; space.
- aStream nextPutAll: name printString; space; space; space.
- aStream nextPutAll: ' price: '; space; space.
- aStream nextPutAll: price printString
- Programming the class DispensingHolder
- Model subclass: #DispensingHolder
- instanceVariableNames: 'items '
- classVariableNames: ''
- poolDictionaries: ''
- category: 'Vending'
- new
- !DispensingHolder methodsFor: 'initialize-release'!
- initialize
- items := OrderedCollection new
- DispensingHolder Connections
- !DispensingHolder methodsFor: 'connections'!
- connectToItem: aItem
- disconnectWithItem: aItem
- items remove: aItem ifAbsent: []
- items
- "Return the current value of items."
- ^items
- Dispensing
- !DispensingHolder methodsFor: 'dispensing'!
- activateDispenser
- dispense: itemToDispense
- "Dispense the item.
- Return true if successful, otherwise false."
- self isEmpty ifTrue: [^false].
- (self itemIsDispensable: itemToDispense) ifFalse:
- self printItem: itemToDispense .
- self activateDispenser.
- self disconnectWithItem: itemToDispense.
- ^true
- Testing and Simulating
- !DispensingHolder methodsFor: 'testing'!
- isEmpty
- itemIsDispensable: itemToDispense
- "Check if the first item and the item to dispense
- are the same object. Be sure to use == rather
- than =."
- ^items first == itemToDispense
- !DispensingHolder methodsFor: 'simulating'!
- printItem: itemToDispense
- Transcript cr.
- Transcript show: 'Releasing ' , itemToDispense printString.
- Example #1
- "EXAMPLE 1"
- "Run the code -- dispense an item (p. 140)"
- | anItem aDispensingHolder |
- aDispensingHolder := DispensingHolder new.
- anItem := Item new.
- anItem name: 'Jolt'; price: 0.75.
- anItem connectToDispensingHolder: aDispensingHolder.
- aDispensingHolder connectToItem: anItem.
- aDispensingHolder dispense: anItem
- Example #2
- "EXAMPLE 2"
- "Run the code -- vend some items (p. 145)"
- | item1 item2 item3 items aDispensingHolder |
- item1 := Item new name: 'Coca-Cola'; price: 0.75.
- item2 := Item new name: 'Pepsi'; price: 0.75.
- item3 := Item new name: 'Jolt'; price: 0.75.
- items := OrderedCollection with: item1 with: item2 with: item3.
- aDispensingHolder := DispensingHolder new.
- items do: [:each |
- aDispensingHolder connectToItem: each.
- each connectToDispensingHolder: aDispensingHolder].
- items do: [:each | each vend].
- Second Part: Adding in the CashDevice
- First Pass Scenario
- I'm an item.
- I know my price.
- I know my corresponding cash device.
- I send it a message to get the amount collected.
- I'm a cash device.
- I get cash.
- I know the amount collected.
- Someone asks me what amount I've collected; I respond with that value.
- I'm an item.
- If the amount collected is greater than or equal to my price
- I tell my dispensing holder to dispense, and
- I tell my cash device to make change.
- Otherwise,
- I return a "not dispensed" result to the sender.
- I'm a cash device.
- I'm an item.
- I return a "dispensed" result to the sender.
- Critique
- o Item does all the work - responsibility not well distributed in design.
- o CashDevice is just a data holder - it has no real functionality.
- An Improved Scenario Script
- I'm an item.
- I know my price.
- I know my corresponding cash device.
- I send a message "got enough (price)" to my cash device.
- I'm a cash device.
- Someone asks me if I've got enough.
- If my amount collected is greater than or equal to the price he gives me,
- then I answer "yes,"
- else I answer "no."
- I'm an item.
- If "yes,"
- I tell my dispensing holder to dispense, and
- I tell my cash device to make change.
- Otherwise,
- I return a "not dispensed" result to the sender.
- I'm a cash device.
- I'm an item.
- I return a "dispensed" result to the sender.
- Building the CashDevice
- Model subclass: #CashDevice
- instanceVariableNames: 'amountCollected '
- classVariableNames: ''
- poolDictionaries: ''
- category: 'Vending'
- CashDevice class methodsFor: 'instance creation'
- new
- CashDevice methodsFor: 'initialize-release'
- initialize
- CashDevice Accessing
- CashDevice methodsFor: 'accessing'
- amountCollected
- "Return the current value of amount collected."
- ^amountCollected
- amountCollected: newValue
- "Set the value of amount collected. Notify my dependents
- that I've changed."
- amountCollected := newValue.
- self changed: #amountCollected
- Connection and Cash Collecting
- Connection accessors
- CashDevice methodsfor: 'cash collecting'
- gotEnough: amountToCollect
- "Return true if the amount collected is greater than
- or equal to the amount to collect."
- ^ amountCollected >= amountToCollect
- return
- "Reset the cash collected. Return false to indicate
- cash not collected."
- self amountCollected: 0
- makeChangeFor: aPrice
- "N.B. This isn't what you'd expect."
- self amountCollected: amountCollected - aPrice
- Simulating
- CashDevice methodsFor: 'simulating'
- addCash
- "Increase my amount collected by some default increment."
- self amountCollected: (amountCollected + self cashIncrement)
- cashIncrement
- Example with CashDevice
- | cashDevice price |
- "N.B. difference between Cash & cash"
- cashDevice := CashDevice new.
- price := 1.0.
- [cashDevice gotEnough: price]
- whileFalse: [Transcript cr; show: 'need more!'.
- Transcript cr; show: 'got enough!'
- Modifications Needed to Item for CashDevice
- Model subclass: #Item
- instanceVariableNames: 'name price cashDevice dispensingHolder'
- "Adding a cashDevice ivar"
- classVariableNames: ''
- poolDictionaries: ''
- category: 'Vending'
- Item methodsFor: 'connections'
- cashDevice
- "Return the current value of cash device."
- ^cashDevice
- connectToCashDevice: aCashDevice
- cashDevice := aCashDevice
- Final Forms of Testing and Vending in Item
- Item methodsFor: 'testing'
- isVendable
- ^cashDevice gotEnough: price
- Item methodsFor: 'vending'
- vend
- | success |
- self isVendable ifFalse:
- [Screen default ringBell. ^false].
- success := dispensingHolder dispense: self.
- success ifFalse: [^false].
- cashDevice makeChangeFor: price.
- ^true.
- The BIG EXAMPLE!
- |item1 item2 item3 items aDispensingHolder aCashDevice |
- item1 := Item new name: 'Coca-Cola'; price: 0.75.
- item2 := Item new name: 'Pepsi'; price: 0.75.
- item3 := Item new name: 'Jolt'; price: 0.75.
- items := Array with: item1 with: item2 with: item3.
- aDispensingHolder := DispensingHolder new.
- aCashDevice := CashDevice new.
- aCashDevice amountCollected: 1.50.
- items do:
- [:anItem |
- aDispensingHolder connectToItem: anItem.
- anItem connectToDispensingHolder: aDispensingHolder.
- anItem connectToCashDevice: aCashDevice].
- "Not both ways on cashDevice."
- items do: [:anItem | anItem vend]
Previous | Next
Last modified at 7/17/97; 2:59:13 PM
Other Links of Interest
College of Computing | EduTech Institute | GVU Center
Mark Guzdial | Papers | CS 2390 Spring '97 Home Page | STABLE | MMC-CaMILE
Slide Master