Monday, March 15, 2021

Vending Machine OOP Design Problem in Java - Part 2

This is the second part of the Java tutorial to show how to design Vending Machine in Java. In the first part, we have discussed the problem statement and the solution itself, but unit testing and design document were still pending, which we'll see in this article.  As I said, there are multiple ways to design a Vending machine in Java, for example, you could have easily used state design patterns to implement a vending machine, in fact, it's one of the best examples of State design patterns. Vending machine behaves differently in different states like return a product if the machine is not empty, otherwise, it just returns coins, so it ideally fits in the state design pattern.

Though, In our solution, I have not used the state design pattern but have just coded the solution with an if-else block. This is easier because of a limited number of states and not many state transitions but in a more real-world scenario, a state design pattern is better as it uses Polymorphism and removes the logic we have put inside the if-else block.

Knowledge of UML is essential for creating a design document for Java programs. UML has several types of the diagram to explain a different aspect of your design like the class diagram to show the dependency of objects, sequence diagram to show how the user will interact with your system and so on.

Since UML is a standard way to design Java applications, it also works as a shared library which many programmers understand. You don't need to explain that class A is related to class B, any Java programmer will figure it out by himself by looking at the class diagram.

Btw, If you are serious about learning object-oriented analysis and design, I suggest you take a look at the Object-Oriented Analysis, Design & Programming with UML course on Udemy.  This course will not only teach you UML but also how to analyze a problem using object-oriented analysis, which is a great skill for programmers and Java developers.





Unit Test for Java Vending Machine Solution

If you follow Test Driven development then you can write the test first but I am not a TDD purist so I create unit tests before and after I code, the bottom line is you must write unit tests for professional code. It's one thing that separates amateur programmers from professional developers. 

If you need help take a look at the Learn Java Unit Testing with Junit & Mockito in 30 Steps course by Ranga Karnam on Udemy. A great course to learn JUnit and Mockito, two of the most essential testing libraries for Java developers. 

Anyway, here is the unit test class for the Vending Machine problem, which tests some behaviors of Vending machine, like buying items with exact change, with more change, less change, canceling an item, resetting the vending machine, etc. I admit that unit tests are not extensive enough and you can add few more test cases to fully test your vending machine solution in Java.

Make sure to have your unit test in the same package but on a different root folder, so that you can test package-private members without mixing production and testing code.

==============================================================
VendingMachineTest.java
===============================================================

package vending;

import org.junit.Ignore;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.;


public class VendingMachineTest {
    private static VendingMachine vm;
   
    @BeforeClass
    public static void setUp(){
        vm = VendingMachineFactory.createVendingMachine();
    }
   
    @AfterClass
    public static void tearDown(){
        vm = null;
    }
   
    @Test
    public void testBuyItemWithExactPrice() {
        //select item, price in cents
        long price = vm.selectItemAndGetPrice(Item.COKE); 
        //price should be Coke's price      
        assertEquals(Item.COKE.getPrice(), price);
        //25 cents paid              
        vm.insertCoin(Coin.QUARTER);                           
       
        Bucket<Item, List<Coin>> bucket = vm.collectItemAndChange();
        Item item = bucket.getFirst();
        List<Coin> change = bucket.getSecond();
       
        //should be Coke
        assertEquals(Item.COKE, item);
        //there should not be any change                              
        assertTrue(change.isEmpty());                              
    }
   
    @Test
    public void testBuyItemWithMorePrice(){
        long price = vm.selectItemAndGetPrice(Item.SODA);
        assertEquals(Item.SODA.getPrice(), price);
       
        vm.insertCoin(Coin.QUARTER);       
        vm.insertCoin(Coin.QUARTER);      
       
        Bucket<Item, List<Coin>> bucket = vm.collectItemAndChange();
        Item item = bucket.getFirst();
        List<Coin> change = bucket.getSecond();
       
        //should be Coke
        assertEquals(Item.SODA, item);
        //there should not be any change                                     
        assertTrue(!change.isEmpty());        
        //comparing change                             
        assertEquals(50 - Item.SODA.getPrice(), getTotal(change));  
       
    }  
  
   
    @Test
    public void testRefund(){
        long price = vm.selectItemAndGetPrice(Item.PEPSI);
        assertEquals(Item.PEPSI.getPrice(), price);       
        vm.insertCoin(Coin.DIME);
        vm.insertCoin(Coin.NICKLE);
        vm.insertCoin(Coin.PENNY);
        vm.insertCoin(Coin.QUARTER);
       
        assertEquals(41, getTotal(vm.refund()));       
    }
   
    @Test(expected=SoldOutException.class)
    public void testSoldOut(){
        for (int i = 0; i < 5; i++) {
            vm.selectItemAndGetPrice(Item.COKE);
            vm.insertCoin(Coin.QUARTER);
            vm.collectItemAndChange();
        }
     
    }
   
    @Test(expected=NotSufficientChangeException.class)
    public void testNotSufficientChangeException(){
        for (int i = 0; i < 5; i++) {
            vm.selectItemAndGetPrice(Item.SODA);
            vm.insertCoin(Coin.QUARTER);
            vm.insertCoin(Coin.QUARTER);
            vm.collectItemAndChange();
           
            vm.selectItemAndGetPrice(Item.PEPSI);
            vm.insertCoin(Coin.QUARTER);
            vm.insertCoin(Coin.QUARTER);
            vm.collectItemAndChange();
        }
    }
   
   
    @Test(expected=SoldOutException.class)
    public void testReset(){
        VendingMachine vmachine = VendingMachineFactory.createVendingMachine();
        vmachine.reset();
       
        vmachine.selectItemAndGetPrice(Item.COKE);
       
    }
   
    @Ignore
    public void testVendingMachineImpl(){
        VendingMachineImpl vm = new VendingMachineImpl();
    }
   
    private long getTotal(List<Coin> change){
        long total = 0;
        for(Coin c : change){
            total = total + c.getDenomination();
        }
        return total;
    }
}

Design Document

Here is a sample design document for the Vending Machine problem. Well, it's not that great but conveys my design decisions in text format. Since in a real test, time is critical you need to balance your time between coding, testing, and designing activity, it's better to choose text over an image. If you are good with UML then adding a class diagram would certainly help here.

In short Design Document in Java Should include
- description of the solution
- design decision and data structures
- All classes and their responsibility
- description of the package
- description of methods
- the motivation of decision e.g. why this design pattern, why enum, why BigDecimal etc?
- Flow Chart of a couple of use cases
- UML Diagram
- Assumption
- Risk
- Benefits

Here is our sample design document, it's very basic because I have generated it very quickly, but it's good for learning and doing Object-Oriented Analysis and design. 

One thing which I would like to add to this document is the UML class diagram because that's another way to convey your design to fellow Java developers. I have added it to this discussion. If you want to learn UML then don't forget to check out UML and Object-Oriented Design Foundations online course by Karoly Nyisztor, a Professional Software Architect on Udemy. 

UML diagram of Vending Machine problem in Java




1. High-level Design

Includes an overview of the problem, not necessary if you are writing this as part of the test because the evaluator should be familiar with problem specification. Important if your design document is intended for someone who is not very familiar with the problem domain.

    - Main interface, classes, and Exceptions
          - VendingMachine - an interface that defines public API of VendingMachine
          - VendingMachineImpl - a general-purpose implementation of the VendingMachine interface
          - Inventory - A type-safe inventory for holding objects, which is an ADAPTER or WRAPPER over java.util.Map
          - Item - type-safe Enum to represent items supported by vending machines.
          - Coin - type-safe Enum to represent coins, which is acceptable by a vending machine.
          - Bucket - A Holder class for holding two types together.
          - SoldOutException - thrown when the user selects a product that is sold out
          - NotSufficientChangeException - thrown when Vending machine doesn't have enough change to support the current transaction.

          - NotFullPaidException - thrown when the user tries to collect an item, without paying the full amount.
     
    - Data structures used
          - Map data structure is used to implement cash and item inventories.
          - The List is used to returning changes because it can contain duplicates, i.e. multiple coins of the same denomination.

   
    - Motivation behind design decisions
         - Factory design pattern is used to encapsulate the creation logic of VendingMachine.
         - Adapter pattern is used to create Inventory by wrapping java.util.Map
         - java.lang.Enum is used to represent Item and Coins, because of the following benefits
                - compile-time safety against entering an invalid item and invalid coin.
                - no need to write code for checking if the selected item or entered coin is valid.
                - reusable and well encapsulated.
         - long is used to represent price and totalSales, which are the amount because we are dealing in cents.
           Not used BigDecimal to represent money, because the vending machine can only hold a limited amount, due to the finite capacity of coin inventory.

         -

2. Low Leven Design

1) Methods
        -  The getChange() method uses a greedy algorithm to find out whether we have sufficient coins to support an amount.

    - Initialization
         - When Vending Machine will be created, it's initialized with the default cash amount and item inventory. current with quantity 5 for each coin and item.



2) Testing Strategy
   - Three primary test cases to testWithExactPrice()testWithMorePrice() and testRefund() to cover general usecase.
   - Negative test cases like testing SoldOutExceptionNotSufficientChangeException or NotFullPaidException
   -

3) Benefits
   - The design uses Abstraction to create reusable, small classes which are easier to read and test.
   - Encapsulating Items and Coins in their respective class makes it easy to add new Coins and Items.
   - Inventory is a general-purpose class and can be used elsewhere, It also encapsulates all inventory operations.

4) Assumption
   - Vending Machine is single-threaded, only one user will operate at a time.
   - A call to reset() will reset the item and balance i.e. make them zero.



. UML Diagram Vending Machine in Java

Here is our UML diagram for this solution, generated in Eclipse by using ObjectAid plugin. It let you create a UML class diagram from Java code itself. This diagram shows that VendingMachineImpl extends or implements the VendingMachine interface. It also shows that it is associated with Item and Inventory classes.

If you are new to Java and not understanding this UML diagram then you should read UML for Java Programmers by Robert C. Martin, one of the best books to learn about UML and the class diagram of  Java programs. It also has similar exercises like designing a coffee machine in Java to test your object-oriented design and analysis skills.

Vending Machine in Java coding, design and UML



That's all on this two-part article on designing and implementing Vending Machine in Java using Object Oriented analysis and design. If you have just started learning OOP or Java, I recommend doing this kind of design-related exercise to improve your design skills in Java. You will face a lot of challenges, which will drive further learning and understanding of the core OOPS concept.


Further Learning
Grokking the System Design Interview
Design Pattern Library
Java Design Patterns - The Complete Masterclass


Other Programming Interview Questions for Java Programmers
  • 10 Best Courses to learn System Design Interview (best courses)
  • 25 Software Design Interview Questions for Programmers (design questions)
  • 18 Java design pattern interviews questions with answers (list)
  • 10 Best SQL and Database Courses for Programmers (best courses)
  • 30 OOP Concept Interview Questions with Answers (list)
  • 40 Core Java Interview Questions from Phone Interviews  (list)
  • 7 Best Courses to learn OOP Design Pattern (best courses)
  • 21 Array Concept Interview Questions in Java? (list)
  • My favorite courses to learn Software Architecture (medium)
  • 12 multi-threading and concurrency Interview Questions (list)
  • 7 Best Courses to learn Data Structure and Algorithms (best courses)
  • 21 frequently asked SQL Queries from Programming Interviews (list)
  • 10 Courses to Crack your Programming Interviews (courses)
  • Top 20 System Design Interview Questions for Programmers (questions)
  • 50+ Data Structure and Algorithms Problems from Interviews (article)
  • Top 5 OOP Design Problems for Beginners (questions)

Thanks for reading this article so far. If you like this Vending Machine OOP design Problem in Java and my solution and 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 are hungry for more of such design problems in Java, you should join the  Grokking the Object-Oriented Design Interview, an interactive course on Educative to practice another similar problem of designing a parking lot system in Java. 

2 comments :

Ashkrit said...

On such type of questions what are your thoughts on implementations
- code solution and then test
- or TDD way

What do company look for ?

javin paul said...

Hello @Ashkrit, good question. IMHO, what matter most is the correct solution within the timeframe. If you feel you are more comfortable with TDD, go with that but if you are not someone who use TDD daily then go with traditional approach of plan + code + Test. Company look for correct solution, if they get more than one correct solution obviously they will select the code which is more maintainable, flexible and clean. It's not worth to leave the problem in half because you don't have enough time.

Post a Comment