Design a Vending Machine in Java - Interview Question (original) (raw)

How do you design a Vending Machine in Java? is one of the popular Java interview questions mostly asked at Senior level Java developer Interviews. In a typical coding interview, you will be given a problem statement to develop a vending machine and within a limited time, usually, 2 to 3 hours you need to produce a design document, working code, and unit test in Java. One of the key advantages of such Java interviews is that you can test many essential skills or a candidate in one go. In order to complete the design, coding, and unit testing of a Vending machine, a candidate needs to be really good in all three departments. Recently, I have also seen this question on Object Oriented Design Interviews and System Design Interviews, where interviewer want to check experienced developers knowledge on various Software architecture components and design decision.

By the way, this kind of real-world problem is also a good exercise to improve your object-oriented analysis and design skills (see here), which is very important if you want to become a good application developer.

By designing a vending machine in Java or any other object-oriented language, you not only learn basics e.g. Encapsulation, Polymorphism, or Inheritance but also learns subtle details of how to use an abstract class and interface (see here) while solving a problem or designing an application.

Usually, this kind of problem also gives you an opportunity to utilize Java design patterns, as in this problem we will be using the Factory method pattern for creating different types of Vending Machine.

I have talked about this question when I shared 20 software design questions in Java (here), and after that, I receive a lot of feedback to provide a solution for that question.

This two-part series of posts will provide a solution to the Vending machine problem in Java. By the way, this problem can be solved in a different way, and you should try to do that before looking into the solution given here.

This is also an opportunity to revisit SOLID and OOP design principles (see here) and get ready to use them in your code. You'll find many of them are applicable when you design a vending machine in Java.

Btw, If you are serious about learning design patterns and principles, I suggest you take a look at the Design Patterns in Java course on Udemy. This course covers both SOLID design principles like Open Closed and Liskov substitution, and all-important Object Oriented design patterns like Decorator, Observer, Chain of Responsibility, and much more.

And, if you are preparing for System design interviews then Grokking the System Design course on DesignGurus.io is one of my personal favorite. It has many similar problem on designing WhatsApp, designing URL Shortener, designing YouTube or Netflix and more. I highly recommend this resource for anyone preparing System design interviews.

And, if you need more object oriented design problems like this then I also recommend you to checkout Codemia.io, a new platform which has biggest collection of easy, intermediate and difficult object oriented problems like how to design an efficient parking lot, design a banking system or design an online shopping system.

Problem Statement

You need to design a Vending Machine which

  1. Accepts coins of 1,5,10,25 Cents i.e. penny, nickel, dime, and quarter.
  2. Allow user to select products Coke(25), Pepsi(35), Soda(45)
  3. Allow user to take refund by canceling the request.
  4. Return the selected product and remaining change if any
  5. Allow reset operation for vending machine supplier.

The requirement statement is the most important part of the problem. You need to read the problem statement multiple times to get a high-level understanding of the problem and what are you trying to solve.

Usually, requirements are not very clear and you need to make a list of your own by reading through the problem statement.

I like point based requirement because it's easy to track. Some of the requirements are also implicit but it's better to make them explicit in your list e.g. In this problem, the vending machine should not accept a request if it doesn't have sufficient change to return.

Unfortunately, there is not many book or courses which teach you these skills, you need to develop them by yourself by doing some real-world work.

Though two of the resources which helped me to improve my object-oriented analysis and design skills are Grokking the Low Level Design Interview Using OOD Principles, an interactive course on Educative which allows you to practice object-oriented problems on the browser, I highly recommend this course to level up your OOP design skill.

Vending Machine OOP Design Problem and Solution in Java

And, the second resource is the Head First Object-Oriented Design and Analysis 1st edition by Brett D. McLaughlin. One of the best books if you don't have much experience in object-oriented programming.

Java Software Design Problem - Vending Machine Solution

Another resource which is very good on developing application and system design skill is UML for Java Programmers by Robert C. Martin, one of my favorite author. I have read several books about him like Clean Code, Clean Coder, and a book on software development using Agile. He is one of the best in teaching the OOP concept.

This book has got a similar problem about designing a coffee machine. So, if you want to practice more or try your object-oriented design skill, you can refer to that problem. It's also a very good learning exercise.

Solution and Coding

My implementation of Java Vending Machine has the following classes and interfaces :

VendingMachine
It defines the public API of a vending machine, usually, all high-level functionality should go in this class

VendingMachineImpl
A sample implementation of Vending Machine

VendingMachineFactory
A Factory class to create different kinds of Vending Machine

Item
Java Enum to represent Item served by Vending Machine

Inventory
Java class to represent an Inventory, used for creating the case and item inventory inside Vending Machine

Coin
Another Java Enum to represent Coins supported by Vending Machine

Bucket
A parameterized class to hold two objects. It's kind of Pair class.

NotFullPaidException
An Exception is thrown by Vending Machine when a user tries to collect an item, without paying the full amount.

NotSufficientChangeException
Vending Machine throws this exception to indicate that it doesn't have sufficient change to complete this request.

SoldOutExcepiton
Vending Machine throws this exception if the user requests a product that is sold out.

How to design vending machine in Java

How to design Vending Machine in Java

Here is the complete code of Vending Machine in Java, make sure to test this code, and let me know if you face any issues. In this part, we will go into low level system design of Vending machine through classes and methods.

VendingMachine.java
The public API of a vending machine, usually all high-level functionality should go in this class.

package vending;

import java.util.List;

/**

VendingMachineImpl.java
A sample implementation of the VendingMachine interface represents a real-world Vending Machine, which you see in your office, bus stand, railway station, and public places.

package vending;

import java.util.ArrayList; import java.util.Collections; import java.util.List;

/**

@Override public long selectItemAndGetPrice(Item item) { if(itemInventory.hasItem(item)){ currentItem = item; return currentItem.getPrice(); } throw new SoldOutException("Sold Out, Please buy another item"); }

@Override
public void insertCoin(Coin coin) {
    currentBalance = currentBalance + coin.getDenomination();
    cashInventory.add(coin);
}

@Override
public Bucket<Item, List<Coin>> collectItemAndChange() {
    Item item = collectItem();
    totalSales = totalSales + currentItem.getPrice();
   
    List<Coin> change = collectChange();
   
    return new Bucket<Item, List<Coin>>(item, change);
}
   
private Item collectItem() throws NotSufficientChangeException,
        NotFullPaidException{
    if(isFullPaid()){
        if(hasSufficientChange()){
            itemInventory.deduct(currentItem);
            return currentItem;
        }           
        throw new NotSufficientChangeException("Not Sufficient change in 
                                                Inventory");
       
    }
    long remainingBalance = currentItem.getPrice() - currentBalance;
    throw new NotFullPaidException("Price not full paid, remaining : ", 
                                      remainingBalance);
}

private List<Coin> collectChange() {
    long changeAmount = currentBalance - currentItem.getPrice();
    List<Coin> change = getChange(changeAmount);
    updateCashInventory(change);
    currentBalance = 0;
    currentItem = null;
    return change;
}

@Override
public List<Coin> refund(){
    List<Coin> refund = getChange(currentBalance);
    updateCashInventory(refund);
    currentBalance = 0;
    currentItem = null;
    return refund;
}


private boolean isFullPaid() {
    if(currentBalance >= currentItem.getPrice()){
        return true;
    }
    return false;
}

  
private List<Coin> getChange(long amount) throws NotSufficientChangeException{

    List<Coin> changes = Collections.EMPTY_LIST;
   
    if(amount > 0){
        changes = new ArrayList<Coin>();
        long balance = amount;
        while(balance > 0){
            if(balance >= Coin.QUARTER.getDenomination() 
                        && cashInventory.hasItem(Coin.QUARTER)){
                changes.add(Coin.QUARTER);
                balance = balance - Coin.QUARTER.getDenomination();
                continue;
               
            }else if(balance >= Coin.DIME.getDenomination() 
                             && cashInventory.hasItem(Coin.DIME)) {
                changes.add(Coin.DIME);
                balance = balance - Coin.DIME.getDenomination();
                continue;
               
            }else if(balance >= Coin.NICKLE.getDenomination() 
                             && cashInventory.hasItem(Coin.NICKLE)) {
                changes.add(Coin.NICKLE);
                balance = balance - Coin.NICKLE.getDenomination();
                continue;
               
            }else if(balance >= Coin.PENNY.getDenomination() 
                             && cashInventory.hasItem(Coin.PENNY)) {
                changes.add(Coin.PENNY);
                balance = balance - Coin.PENNY.getDenomination();
                continue;
               
            }else{
                throw new NotSufficientChangeException("NotSufficientChange,
                                   Please try another product");
            }
        }
    }
   
    return changes;
}

@Override
public void reset(){
    cashInventory.clear();
    itemInventory.clear();
    totalSales = 0;
    currentItem = null;
    currentBalance = 0;
} 
   
public void printStats(){
    System.out.println("Total Sales : " + totalSales);
    System.out.println("Current Item Inventory : " + itemInventory);
    System.out.println("Current Cash Inventory : " + cashInventory);
}   


private boolean hasSufficientChange(){
    return hasSufficientChangeForAmount(currentBalance
                    - currentItem.getPrice());
}

private boolean hasSufficientChangeForAmount(long amount){
    boolean hasChange = true;
    try{
        getChange(amount);
    }catch(NotSufficientChangeException nsce){
        return hasChange = false;
    }
   
    return hasChange;
}

private void updateCashInventory(List change) {
    for(Coin c : change){
        cashInventory.deduct(c);
    }
}

public long getTotalSales(){
    return totalSales;
}

}

VendingMachineFactory.java
A Factory class to create different kinds of Vending Machine

package vending;

/**

Item.java
Java Enum to represent Item served by Vending Machine

package vending; /**

}

Coin.java
Another Java Enum to represent Coins supported by Vending Machine

package vending;

/**

}

Inventory.java
A Java class to represent an Inventory, used for creating the case and item inventory inside Vending Machine.

package vending; import java.util.HashMap; import java.util.Map;

/**

}

Bucket.java
A parameterized utility class to hold two objects.

package vending; /**

}

NotFullPaidException.java
An Exception, thrown by Vending Machine when a user tries to collect an item, without paying the full amount.

package vending; public class NotFullPaidException extends RuntimeException { private String message; private long remaining;

public NotFullPaidException(String message, long remaining) {
    this.message = message;
    this.remaining = remaining;
}

public long getRemaining(){
    return remaining;
}

@Override
public String getMessage(){
    return message + remaining;
} 

}

NotSufficientChangeException.java
Vending Machine throws this exception to indicate that it doesn't have sufficient change to complete this request.

package vending; public class NotSufficientChangeException extends RuntimeException { private String message;

public NotSufficientChangeException(String string) {
    this.message = string;
}

@Override
public String getMessage(){
    return message;
}

}

SoldOutException.java
The Vending Machine throws this exception if the user requests a product that is sold out

package vending; public class SoldOutException extends RuntimeException { private String message;

public SoldOutException(String string) {
    this.message = string;
}

@Override
public String getMessage(){
    return message;
}

}

That's all in this first part of how to design a vending machine in Java. In this part, we have solved the problem by creating all the classes and writing all code, but the unit test and design document are still pending, which you will see in the second part of this article.

If you want you can try to run this problem by creating the Unit test, or maybe make it an application by using a thread and then use a different thread to act as the user.

Further Learning

Object oriented Design Problems at Codemia.io

Exponent Software Design course

Other Programming Interview Questions for Java Programmers

Thanks for reading this article so far. If you like this Vending Machine design Problem in Java and my explanation, then please share it with your friends and colleagues. If you have any questions or doubt then, please drop a note,

P. S. - If you need more OOP design questions for practice then, I suggest you check out the Grokking the Object-Oriented Design Interview course on DesignGurus.io, an interactive learning platform. This course is designed by the hiring managers of Google, Facebook, Microsoft, and Amazon and contains solutions to some of the frequently asked object-oriented design questions from these tech giants.

P.P.S. - If you are preparing for Object oriented analysis and design interviews then I also recommend you to checkout, Codemia.io, a new platform which has biggest collection of easy, intermediate and difficult object oriented problems like how to design an efficient parking lot, design a banking system or design an online shopping system.