The VendingMachine will manage an inventory of products, each having a unique code number and associated price. The machine operates through a series of states that define its current operational mode.
Based on the requirements and use cases, identify the main objects of the system...
Determine how these objects will interact with each other to fulfill the use cases...
Design inheritance trees where applicable to promote code reuse and polymorphism. This step involves identifying common attributes and behaviors that can be abstracted into parent classes...
Consider using design patterns (e.g., Factory, Singleton, Observer, Strategy) that fit the problem
Singelton Design Pattern
Class VendingMachine {
private static instance=null;
private VendingMachine(){}
public VendingMachine getInstance(){
if(instance==null)return new VendingMachine();
else
return instance;
}
//other methods
}
In the vending machine system, the State Design Pattern has been employed to manage the machine's operations through distinct states. This pattern ensures that the vending machine can perform specific operations allowed in one state while preventing any operations associated with other states. This structured approach has the following benefits:
interface State{
//methods
selectProduct();
refundMoney();
dispenseProduct();
}
class IdelState implements State{
//overide the mehtods
// works for only specefic operations that is acceptable for IdelState and for other operation will throw
}
class DispenseState Implements State{}
class SelectState Implements State{}
Vending Machine
public class VendingMachine{
States state;
Inventory inventory;
List<Coin> coinList;
public List<Coin> getCoinList() {
return coinList;
}
public VendingMachine() {
this.coinList = new ArrayList<>();
this.inventory = new Inventory(10);
this.state = (States) new IdleState();
}
public void setCoinList(List<Coin> coinList) {
this.coinList = coinList;
}
public States getState() {
return state;
}
public void setState(States state) {
this.state = state;
}
public Inventory getInventory() {
return inventory;
}
public void setInventory(Inventory inventory) {
this.inventory = inventory;
}
}
ItemShelf
public class ItemShelf {
int code;
Item item;
boolean soldOut;
// public ItemShelf(Item item, int code, boolean soldOut) {
// this.item = item;
// this.code = code;
// this.soldOut = soldOut;
// }
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
public boolean isSoldOut() {
return soldOut;
}
public void setSoldOut(boolean soldOut) {
this.soldOut = soldOut;
}
}
Item
public class Item
{
ItemType type;
int price;
public ItemType getType() {
return type;
}
public void setType(ItemType type) {
this.type = type;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Inventory
public class Inventory
{
ItemShelf[] inventory=null;
// intialize inventory
// add item
// get Item By CodeNumber
// updateSoldout
public ItemShelf[] getInventory() {
return inventory;
}
public void setInventory(ItemShelf[] inventory) {
this.inventory = inventory;
}
public Inventory(int ItemCount) {
inventory = new ItemShelf[ItemCount];
intialInventory();
}
public void intialInventory() {
int n=inventory.length;
int startCode=100;
for(int i=0;i<n;i++) {
ItemShelf itemShelf=new ItemShelf();
itemShelf.code=startCode;
itemShelf.item=null;
itemShelf.soldOut=true;
startCode++;
}
}
public void addItem(int codeNumber, Item item)
{
for(int i=0;i<inventory.length;i++) {
if(inventory[i].code==codeNumber) {
inventory[i].item=item;
inventory[i].soldOut=false;
}
}
}
public Item getItem(int code) throws Exception {
for(int i=0;i<inventory.length;i++) {
if(inventory[i].code==code&& inventory[i].soldOut!=false) {
return inventory[i].item;
}
else
throw new Exception("Item already soled");
}
return null;
}
public void updateSoldOut(int codeNumber)
{
for(int i=0;i<inventory.length;i++) {
if(inventory[i].code==codeNumber) {
inventory[i].soldOut=true;
}
}
}
}
Coins
public enum Coin {
PENNY(1),
NICKEL(5),
DIME(10),
QUARTER(25);
public int value;
Coin(int value) {
this.value = value;
}
}
ItemType
public enum ItemType
{
COKE,
PEPSI,
SODA,
CHIPS,
CHOCO
}
Dispense State
public class DispenseState implements States {
DispenseState (VendingMachine machine, int codeNumber)throws Exception
{
System.out.println("Currently Vending machine is in DispenseState");
dispenseProduct(machine, codeNumber);
}
@Override
public void clickOnInsertCoinButton(VendingMachine machine) throws Exception{
throw new Exception("insert coin button can not clicked on Dispense state");
}
@Override
public void clickOnStartProductSelectionButton(VendingMachine machine) throws Exception {
throw new Exception("product selection buttion can not be clicked in Dispense state");
}
@Override
public void insertCoin(VendingMachine machine, Coin coin) throws Exception{
throw new Exception("coin can not be inserted in Dispense state");
}
@Override
public void chooseProduct(VendingMachine machine, int codeNumber) throws Exception{
throw new Exception("product can not be choosen in Dispense state");
}
@Override
public int getChange(int returnChangeMoney) throws Exception{
throw new Exception("change can not returned in Dispense state");
}
@Override
public List<Coin> refundFullMoney(VendingMachine machine) throws Exception{
throw new Exception("refund can not be happen in Dispense state");
}
@Override
public Item dispenseProduct(VendingMachine machine, int codeNumber) throws Exception {
// return null;
System.out.println("Product has been dispensed");
Item item = machine.getInventory().getItem(codeNumber);
machine.getInventory().updateSoldOut(codeNumber);
machine.setState(new IdleState(machine));
return item;
}
@Override
public void updateInventory(VendingMachine machine, Item item, int codeNumber) throws Exception {
}
}
HasMoneyState
public class HasMoneyState implements States
{
public HasMoneyState(){
System.out.println("Currently Vending machine is in HasMoneyState");
}
@Override
public void clickOnInsertCoinButton(VendingMachine machine) throws Exception {
return;
}
@Override
public void clickOnStartProductSelectionButton(VendingMachine machine) throws Exception {
machine.setState(new SelectionState());
}
@Override
public void insertCoin(VendingMachine machine, Coin coin) throws Exception {
System.out.println("Accepted the coin");
machine.getCoinList().add(coin);
}
@Override
public void chooseProduct(VendingMachine machine, int codeNumber) throws Exception {
throw new Exception("you need to click on start product selection button first");
}
@Override
public int getChange(int returnChangeMoney) throws Exception {
throw new Exception("you can not get change in hasMoney state");
}
@Override
public Item dispenseProduct(VendingMachine machine, int codeNumber) throws Exception {
throw new Exception("product can not be dispensed in hasMoney state");
}
@Override
public List<Coin> refundFullMoney(VendingMachine machine) throws Exception {
System.out.println("Returned the full amount back in the Coin Dispense Tray");
machine.setState(new IdleState(machine));
return machine.getCoinList();
}
@Override
public void updateInventory(VendingMachine machine, Item item, int codeNumber) throws Exception {
throw new Exception("you can not update inventory in hasMoney state");
}
}
IdleState
public class IdleState implements States {
public IdleState(){
System.out.println("Currently Vending machine is in IdleState");
}
public IdleState(VendingMachine machine){
System.out.println("Currently Vending machine is in IdleState");
machine.setCoinList(new ArrayList<>());
}
@Override
public void clickOnInsertCoinButton(VendingMachine machine) throws Exception{
machine.setState(new HasMoneyState());
}
@Override
public void clickOnStartProductSelectionButton(VendingMachine machine) throws Exception {
throw new Exception("first you need to click on insert coin button");
}
@Override
public void insertCoin(VendingMachine machine, Coin coin) throws Exception{
throw new Exception("you can not insert Coin in idle state");
}
@Override
public void chooseProduct(VendingMachine machine, int codeNumber) throws Exception{
throw new Exception("you can not choose Product in idle state");
}
@Override
public int getChange(int returnChangeMoney) throws Exception{
throw new Exception("you can not get change in idle state");
}
@Override
public List<Coin> refundFullMoney(VendingMachine machine) throws Exception{
throw new Exception("you can not get refunded in idle state");
}
@Override
public Item dispenseProduct(VendingMachine machine, int codeNumber) throws Exception{
throw new Exception("proeduct can not be dispensed idle state");
}
@Override
public void updateInventory(VendingMachine machine, Item item, int codeNumber) throws Exception {
machine.getInventory().addItem( codeNumber,item);
}
}
Selection State
public class SelectionState implements States {
public SelectionState(){
System.out.println("Currently Vending machine is in SelectionState");
}
@Override
public void clickOnInsertCoinButton(VendingMachine machine) throws Exception{
throw new Exception("you can not click on insert coin button in Selection state");
}
@Override
public void clickOnStartProductSelectionButton(VendingMachine machine) throws Exception {
return;
}
@Override
public void insertCoin(VendingMachine machine, Coin coin) throws Exception{
throw new Exception("you can not insert Coin in selection state");
}
@Override
public void chooseProduct(VendingMachine machine, int codeNumber) throws Exception{
//1. get item of this codeNumber
Item item = machine.getInventory().getItem(codeNumber);
//2. total amount paid by User
int paidByUser = 0;
for(Coin coin : machine.getCoinList()){
paidByUser = paidByUser + coin.value;
}
//3. compare product price and amount paid by user
if(paidByUser < item.getPrice()) {
System.out.println("Insufficient Amount, Product you selected is for price: " + item.getPrice() + " and you paid: " + paidByUser);
refundFullMoney(machine);
throw new Exception("insufficient amount");
}
else if(paidByUser >= item.getPrice()) {
if(paidByUser > item.getPrice()) {
getChange(paidByUser-item.getPrice());
}
machine.setState(new DispenseState(machine, codeNumber));
}
}
@Override
public int getChange(int returnExtraMoney) throws Exception{
//actual logic should be to return COINs in the dispense tray, but for simplicity i am just returning the amount to be refunded
System.out.println("Returned the change in the Coin Dispense Tray: " + returnExtraMoney);
return returnExtraMoney;
}
@Override
public List<Coin> refundFullMoney(VendingMachine machine) throws Exception{
System.out.println("Returned the full amount back in the Coin Dispense Tray");
machine.setState(new IdleState(machine));
return machine.getCoinList();
}
@Override
public Item dispenseProduct(VendingMachine machine, int codeNumber) throws Exception{
throw new Exception("product can not be dispensed Selection state");
}
@Override
public void updateInventory(VendingMachine machine, Item item, int codeNumber) throws Exception {
throw new Exception("Inventory can not be updated in Selection state");
}
}
Check and explain whether your design adheres to solid principles (Ask interviewer what SOLID principle is if you can not recall it.)...
IdleState, SelectionState, DispenseState, HasMoneyState) has its own independent implementation, ensuring clear responsibilities.VendingMachine to accept any child state object without causing application errors, maintaining functionality.State interface that encompasses only the necessary methods required for concrete classes.State interface) rather than concrete implementations, promoting flexibility and decoupling components.Explain how your design can handle changes in scale and whether it would be easily to extend with new functionalities...
The modular nature of the State Design Pattern allows for the easy introduction of additional states, such as Maintenance Mode and Out Of Order, without disrupting existing functionality.
We have extensively utilized ENUs for ItemType and CoinType, enhancing extensibility. New types can be added seamlessly without modifying or retesting existing code.
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...
Item, a unique code, and indicates if the item is sold out.ItemShelf objects, allowing for item management.Critically examine your design for any flaws or areas for future improvement...
In Future i want to add the
We will implement a Notification System to alert administrators in situations such as:
Using the Observer Design Pattern, the vending machine will notify all registered observers when these events occur, enabling quick action via alerts (like email or SMS).
We will introduce a Bill Generation feature that provides users with a receipt for their purchase, including:
This enhances transparency and record-keeping for both users and administrators.