Design an object-oriented system for a network of automated coffee kiosks spread across multiple locations. Customers can place orders through a mobile app or web interface, selecting their preferred kiosk location for pickup. Each kiosk can prepare various beverages using available ingredients. The system must track ingredient quantities at each location and automatically mark beverages as unavailable when ingredients run low. Staff should only need to physically refill ingredients when inventory alerts trigger.
Your design should support the following operations:
Ingredient Management
Recipe System
Kiosk Operations
Order Management
Central Coordination
Consider these classes in your design:
Ingredient: Represents a single ingredient with quantity tracking and consumption logic
Recipe: Defines how to make a beverage with required ingredients and proportions
Beverage: Represents a drink that can be ordered, linking to its recipe
InventoryManager: Manages ingredient stock at a single location with thread-safe operations
Order: Captures customer order details with status tracking
Kiosk: Represents a physical kiosk location with its inventory and order processing capability
CentralSystem: Coordinates multiple kiosks and provides system-wide views
``
Hint 1: Core Classes Start by identifying the key entities in the system:
- Ingredient: The fundamental building block with quantity and unit
- Recipe: A blueprint mapping ingredient names to required amounts
- InventoryManager: Handles stock levels with thread-safe operations
- Kiosk: Owns an inventory manager and processes orders
- Order: Contains all information about a customer request
- CentralSystem: Maintains the registry of all kiosks and recipes
Think about the relationships: A kiosk HAS-A inventory manager, recipes USE ingredients, orders REFERENCE both beverages and kiosks.
Hint 2: Inventory Management The inventory management is critical for correctness:
- Use a dictionary to map ingredient names to their current quantities
- Implement atomic operations for checking and consuming ingredients
- Consider using locks or other synchronization primitives to prevent race conditions
- When checking if a beverage can be made, verify ALL ingredients are sufficient
- Only deduct inventory after confirming the entire recipe can be fulfilled
- Track minimum thresholds separately from current quantities
The check-and-consume operation should be atomic to prevent two orders from depleting the same ingredient below zero.
Hint 3: Order Processing Flow Design the order lifecycle carefully:
- Validation: Check if the requested kiosk exists and the beverage recipe is registered
- Availability Check: Verify the kiosk has sufficient ingredients
- Reservation: Optionally lock ingredients for the order
- Preparation: Deduct ingredients and mark order as preparing
- Completion: Mark order ready for pickup
Consider what happens if an order fails at any stage. Should ingredients be rolled back? How do you track order status?
Use enums or constants for order states: PLACED, PREPARING, READY, COMPLETED, CANCELLED, FAILED.
Hint 4: Scalability Patterns For a production-ready system, consider these patterns:
Observer Pattern: Notify subscribers when inventory drops below thresholds Factory Pattern: Create different beverage types with a common interface Strategy Pattern: Different pricing strategies for different locations or times Singleton Pattern: Ensure only one central system instance exists
For thread safety in a multi-threaded environment:
- Use threading locks around inventory modifications
- Consider using queues for order processing
- Implement retry logic for transient failures
Full Solution ``
Solution Explanation
Architecture Overview:
The solution follows object-oriented design principles with clear separation of concerns:
- Ingredient Class: Encapsulates quantity tracking and threshold monitoring for a single ingredient. Provides methods to check sufficiency, consume amounts, and detect when refills are needed.
- Recipe Class: Acts as a blueprint defining ingredient requirements. Supports size scaling through multipliers, making it easy to offer small, medium, and large options without duplicating recipe data.
- Beverage Class: Links a recipe to a product name and pricing. Separates the "what to make" (recipe) from the "what to sell" (beverage) concerns.
- InventoryManager Class: Provides thread-safe inventory operations using locks. The critical
consume_for_recipemethod atomically checks and consumes ingredients, preventing race conditions where two orders might both think they can use the last of an ingredient.- Order Class: Tracks the complete lifecycle of a customer order with status updates and timestamps. Uses UUID for unique identification.
- Kiosk Class: Represents a physical location with its own inventory. Processes orders locally and maintains order history. Delegates inventory concerns to InventoryManager.
- CentralSystem Class: Provides the coordination layer managing multiple kiosks, global recipe catalog, and system-wide queries. Acts as the entry point for order placement and status tracking.
Key Design Decisions:
- Thread Safety: Used locks in InventoryManager to handle concurrent access safely
- Atomic Operations: Check-and-consume happens atomically to prevent overselling
- Status Tracking: Enum-based order states provide type safety and clarity
- Composition Over Inheritance: Kiosk HAS-A InventoryManager rather than extending it
- Separation of Concerns: Each class has a single, well-defined responsibility
Time and Space Complexity:
- Order placement: O(1) - just creates an order object
- Order processing: O(I) where I is ingredients in recipe - must check each ingredient
- Availability check: O(B × I) where B is beverages and I is ingredients per recipe
- Refill alerts: O(K × I) where K is kiosks and I is ingredients per kiosk
- Space: O(K × I + R × I + O) for kiosks, recipes, and orders
Extensions for Production:
- Add database persistence for orders and inventory
- Implement message queues for asynchronous order processing
- Add payment processing integration
- Implement customer notification system (SMS/email when order ready)
- Add analytics for popular drinks and usage patterns
- Implement predictive restocking based on historical data
- Add user authentication and loyalty programs
- Support customizations (extra shot, sugar-free, etc.)
# Initialize central system
central = CentralSystem()
# Register kiosks
kiosk1 = central.register_kiosk("K001", "Downtown Plaza")
kiosk2 = central.register_kiosk("K002", "Airport Terminal")
# Add initial inventory to kiosk1
kiosk1.add_ingredient("coffee_beans", 1000, "grams", min_threshold=100)
kiosk1.add_ingredient("milk", 2000, "ml", min_threshold=200)
kiosk1.add_ingredient("sugar", 500, "grams", min_threshold=50)
kiosk1.add_ingredient("water", 5000, "ml", min_threshold=500)
# Create beverage recipes
latte_recipe = Recipe("Latte", {
"coffee_beans": 18,
"milk": 200,
"water": 30
})
central.add_recipe(latte_recipe)
cappuccino_recipe = Recipe("Cappuccino", {
"coffee_beans": 18,
"milk": 120,
"water": 30
})
central.add_recipe(cappuccino_recipe)
# Customer places order
order1 = central.place_order(
customer_id="C12345",
beverage_name="Latte",
size="medium",
kiosk_id="K001"
)
# Process the order
if kiosk1.can_fulfill(order1):
kiosk1.prepare_beverage(order1)
print(f"Order {order1.id} ready for pickup")
else:
print("Insufficient ingredients")
# Check for refill alerts
alerts = central.get_refill_alerts()
for alert in alerts:
print(f"Kiosk {alert.kiosk_id}: {alert.ingredient} needs refill")