problemmediumooddesign-a-vending-machine-like-coffee-vending-machinedesign a vending machine like coffee vending machinedesignavendingmachinelikecoffeevendingmachine

Object-Oriented Design for Vending Machine (Coffee)

MediumUpdated: Jan 1, 2026

Problem

Design an object-oriented vending machine for beverages (coffee) that allows a user to select a product, insert coins/bills, dispense the product, and return change. Keep internal preparation details encapsulated while exposing a simple user workflow.

Solution

1. Requirements Analysis

Functional Requirements:

  • Display available products with prices on a selection panel.
  • Allow product selection and start/cancel of a transaction.
  • Accept coins/bills, track current balance, and compute change.
  • Dispense the selected product and return change.
  • Encapsulate product preparation steps (e.g., coffee preparation) from the user.

Non-Functional Requirements:

  • Reliability: consistent transactions and correct change dispensing.
  • Usability: clear prompts and error handling for insufficient funds.
  • Extensibility: support adding new products or payment types.

2. Use Case Diagram

graph TB
  subgraph "Vending Machine System"
    UC_Start("Start Transaction")
    UC_Select("Select Product")
    UC_Insert("Insert Coin/Bill")
    UC_Dispense("Dispense Product")
    UC_Change("Dispense Change")
    UC_Cancel("Cancel Transaction")
  end
  User([User]) --> UC_Start
  User([User]) --> UC_Select
  User([User]) --> UC_Insert
  User([User]) --> UC_Dispense
  User([User]) --> UC_Change
  User([User]) --> UC_Cancel
  style User fill:#4CAF50,color:#fff

3. Class Diagram

Core Classes:

  • VendingMachine: Orchestrates transaction flow and delegates to components.
  • Controller: Manages balance, selection, and dispense/cancel logic.
  • SelectionPanel: Displays products and captures user choice.
  • CoinCollector: Accepts and tracks inserted money, supports returns.
  • CoinDispenser: Returns change to the user.
  • ProductDispenser: Dispenses selected product.
  • Product: Value object for product name and price.
classDiagram
  class VendingMachine {
    -Controller controller
    -SelectionPanel selectionPanel
    -CoinCollector coinCollector
    -CoinDispenser coinDispenser
    -ProductDispenser productDispenser
    +startTransaction()
    +selectProduct(productId)
    +insertCoin(coinValue)
    +dispenseProduct()
    +dispenseChange()
    +cancelTransaction()
  }

  class Controller {
    -int currentBalance
    -Product selectedProduct
    +processSelection(productId)
    +processCoinInsertion(coinValue)
    +handleDispenseProduct()
    +handleDispenseChange()
    +handleCancelTransaction()
  }

  class SelectionPanel {
    -List~Product~ products
    +displayProducts()
    +chooseProduct(productId)
  }

  class CoinCollector {
    -int collectedAmount
    +collectCoin(coinValue)
    +returnCoins()
    +getCollectedAmount()
  }

  class CoinDispenser {
    +dispenseChange(changeAmount)
  }

  class Product {
    -String name
    -float price
    +getName()
    +getPrice()
  }

  class ProductDispenser {
    +dispenseProduct(productId)
  }

  VendingMachine "1" *-- "1" Controller : "controls"
  VendingMachine "1" *-- "1" SelectionPanel : "uses"
  VendingMachine "1" *-- "1" CoinCollector : "uses"
  VendingMachine "1" *-- "1" CoinDispenser : "uses"
  VendingMachine "1" *-- "1" ProductDispenser : "uses"
  VendingMachine "1" o-- "*" Product : "offers"
  SelectionPanel "1" o-- "*" Product : "displays"
  Controller "1" --> "*" Product : "selects"

4. Activity Diagrams

Purchase Flow

graph TB
  A[User starts transaction] --> B[User selects product]
  B --> C[User inserts money]
  C --> D{Sufficient balance}
  D -- Yes --> E[Dispense product]
  E --> F[Dispense change]
  F --> G[End]
  D -- No --> H[Prompt for more money or cancel]

Cancel Transaction

graph TB
  X[User cancels] --> Y[Controller stops transaction]
  Y --> Z[CoinCollector returns coins]
  Z --> W[End]

5. High-Level Code Implementation

Java

public abstract class VendingMachine {
    protected Controller controller;
    protected SelectionPanel selectionPanel;
    protected CoinCollector coinCollector;
    protected CoinDispenser coinDispenser;
    protected ProductDispenser productDispenser;

    public abstract void startTransaction();
    public abstract void selectProduct(String productId);
    public abstract void insertCoin(int coinValue);
    public abstract void dispenseProduct();
    public abstract void dispenseChange();
    public abstract void cancelTransaction();
}

public class Controller {
    private int currentBalance;
    private Product selectedProduct;

    public void processSelection(String productId) {}
    public void processCoinInsertion(int coinValue) {}
    public void handleDispenseProduct() {}
    public void handleDispenseChange() {}
    public void handleCancelTransaction() {}
}

public class SelectionPanel {
    private java.util.List<Product> products;
    public void displayProducts() {}
    public void chooseProduct(String productId) {}
}

public class CoinCollector {
    private int collectedAmount;
    public void collectCoin(int coinValue) {}
    public void returnCoins() {}
    public int getCollectedAmount() { return collectedAmount; }
}

public class CoinDispenser {
    public void dispenseChange(int changeAmount) {}
}

public class ProductDispenser {
    public void dispenseProduct(String productId) {}
}

public class Product {
    private String name;
    private float price;
    public String getName() { return name; }
    public float getPrice() { return price; }
}

// Example specialization encapsulating preparation details
public class CoffeeVendingMachine extends VendingMachine {
    @Override public void startTransaction() {}
    @Override public void selectProduct(String productId) {}
    @Override public void insertCoin(int coinValue) {}
    @Override public void dispenseProduct() { prepareCoffee(); }
    @Override public void dispenseChange() {}
    @Override public void cancelTransaction() {}

    private void prepareCoffee() {
        // internal coffee preparation steps
    }
}

Python

from __future__ import annotations
from dataclasses import dataclass
from typing import List

class VendingMachine:
    def start_transaction(self) -> None: ...
    def select_product(self, product_id: str) -> None: ...
    def insert_coin(self, coin_value: int) -> None: ...
    def dispense_product(self) -> None: ...
    def dispense_change(self) -> None: ...
    def cancel_transaction(self) -> None: ...

class Controller:
    def __init__(self) -> None:
        self.current_balance: int = 0
        self.selected_product: Product | None = None
    def process_selection(self, product_id: str) -> None: ...
    def process_coin_insertion(self, coin_value: int) -> None: ...
    def handle_dispense_product(self) -> None: ...
    def handle_dispense_change(self) -> None: ...
    def handle_cancel_transaction(self) -> None: ...

class SelectionPanel:
    def __init__(self, products: List[Product]) -> None:
        self.products = products
    def display_products(self) -> None: ...
    def choose_product(self, product_id: str) -> None: ...

class CoinCollector:
    def __init__(self) -> None:
        self.collected_amount: int = 0
    def collect_coin(self, coin_value: int) -> None: ...
    def return_coins(self) -> None: ...
    def get_collected_amount(self) -> int: return self.collected_amount

class CoinDispenser:
    def dispense_change(self, change_amount: int) -> None: ...

class ProductDispenser:
    def dispense_product(self, product_id: str) -> None: ...

@dataclass
class Product:
    name: str
    price: float

Comments