This Vending machine should be designed with a physical vending machines use cases in mind. The vending machine should only accept one request at a time like a real vending machine. Once a transaction has been started, the vending machine should process that transaction in its entirety before being able to process another transaction.
The vending machine should be able to process multiple types of payments. If an item is not in stock, it should notify the user, and cancel the transaction. Upon errors occurring within a transaction, the vending machine should enter an error state and notify an administrator. The vending machine can not enter back into a normal state until an administrator pushes it back to a normal state.
Once an item has been entered into the system, a transaction has been started. Another item can not be processed until the current transaction is completed. A user can abort a transaction either while the machine is checking if the item is in stock, or if they are in the process of paying for the item.
See The class diagram for relationships
See the class diagram for the hierarchy
This design utilizes the state design pattern to control the flow of operations within the vending machine system. The vending machine itself is a singleton as well as the payment manager class. The payments classes implement a strategy pattern where the concrete implementation of processPayment is left to each individual payment subclasses. An observer pattern can be implemented to notify the administrator when there is an error state
Attributes: For each class, define the attributes (data) it will hold...
Methods: Define the methods (functions) that operate on the attributes. Ensure they align with the object's responsibilities and adhere to the principle of encapsulation.
class VendingMachineState:
def selectProduct(self, productID):
print("Operation not allowed")
def dispenseProduct(self):
print("Operation not allowed")
def cancelTransaction(self):
print("Operation not allowed")
def processPayment(self):
print("Operation not allowed")
class IdleState(State):
def init(self, machine):
self.machine = machine
def selectProduct(self, productID):
if self.machine.products[productID][1] > 0:
self.machine.selectedProduct = productID
return 0
else:
return 1
class ProcessPaymentState(State):
def init(self, machine, paymentManager):
self.machine = machine
self.paymentManager = paymentManager
def processPayment(self,paymentInfo):
if self.machine.selectedProduct == None:
return 1
else:
self.machine.payment = PaymentFactory.getPayment(paymentInfo)
return paymentManager.processPayment() == 0:
def canceltransaction(self):
self.machine.selectedProduct = None
self.machine.state = self.machine.idleState
class DispenseProductState(self):
def init(self, machine, paymentManager):
self.machine = machine
self.paymentManager = paymentManager
def dispenseProduct(self):
#Dispense product
if dispensingErrorOccured:
self.machine.state = self.machine.ErrorState
self.machine.errorMsg = errorMsg
self.machine.products[self.machine.selectedProduct][1] -= 1
def canceltransaction(self):
self.paymentManager.refundPayment(self.machine.payment)
#reset machine and push to idle state
class VendingMachine:
def init(self, numRows, numColumns):
self.products = GenerateProductsDict(numRows, numColumns)
self.paymentManager = PaymentManager()
#generate state instances
self.selectedProduct = None
self.payment = None
self.errorMsg = None
self.state = self.idleState
def addProduct(self, productName, productPrice,quantity):
productID = getOpenSpotInMachine()
if productID == None:
self.state = self.errorState
else:
self.products[productID] = [VendingMachineItem(productName, productPrice, productID), quantity]
def selectProduct(self, productID):
self.state.selectProduct(productID)
def dispenseProduct(self):
self.state.dispenseProduct()
def cancelTransaction(self):
self.state.cancelTransaction()
def processPayment(self):
self.state.processPayment()
class VendingMachineItem:
def init(self, productname, productPrice, productID):
self.productName = productname
self.productPrice = productPrice
self.productID = productID
class Payment:
def processPayment(self):
pass
class PaymentFactory:
@staticmethod
def getPayment(method):
#generatePayment logic and return payment object
class CoinPayment(Payment):
def init(self, amount):
self.amount = amount
def processPayment(self):
#payment logic here
if successful:
return 0
else:
return 1
class CashPayment(Payment):
def init(self, amount):
self.amount = amount
def processPayment(self):
#payment logic here
if successful:
return 0
else:
return 1
class CardPayment(Payment):
def init(self, amount):
self.amount = amount
def processPayment(self):
#payment logic here
if successful:
return 0
else:
return 1
#Administrator observer code goes here
Check and explain whether your design adheres to solid principles (Ask interviewer what SOLID principle is if you can not recall it.)...
new payment methods can be easily added by creating a new class that inherits the Payment base class and adding appropriate logic to the Payment Factory. new vending machine items can be easily added using the addProduct method. this system is designed to only handle one transaction at a time but that is fine as this is simulating a vending machine system.
Try creating a class, flow, state and/or sequence diagram using the diagramming tool. Mermaid flow diagrams can be used to represent system use cases. You can ask the interviewer bot to create a starter diagram if unfamiliar with the tool. Briefly explain your diagrams if necessary...
Critically examine your design for any flaws or areas for future improvement...