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