AtomicInteger for safe concurrent updates.OrderStatus.ConcurrentHashMap.StorageLocation is uniquely identified by its id.Warehouse class maintains a list of StorageLocation objects.StorageLocation but are managed via inventory.Warehouse owns an instance of InventoryManager, which manages the inventory for the entire warehouse.Warehouse owns an instance of OrderManager, responsible for creating and processing orders.InventoryManager manages multiple Item objects in a thread-safe manner using a ConcurrentHashMap.Order can contain multiple Item objects with associated quantities.Item can belong to multiple Order objects.Map<Item, Integer> in the Order class.Warehouse depends on the OptimizationStrategy interface to suggest optimal storage locations.Warehouse
|-- owns --> InventoryManager
|-- owns --> OrderManager
|-- owns --> List<StorageLocation>
|-- depends_on --> OptimizationStrategy
InventoryManager
|-- manages --> Map<String, Item>
OrderManager
|-- manages --> List<Order>
Order
|-- contains --> Map<Item, Integer>
StorageLocation
|-- contains --> Item (indirectly via InventoryManager)
OptimizationStrategy
|-- evaluates --> List<StorageLocation> for Item
OptimizationStrategy interface to allow flexibility in implementing different storage optimization strategies.InventoryManager, OrderManager, and ShipmentManager to ensure a single instance per warehouse.Order and Shipment objects dynamically./**
* Refined Design for Warehouse Management System
* Following SOLID principles, addressing concurrency for an in-memory system, and incorporating feedback.
*/
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
// Enums for Order and Shipment States
enum OrderStatus { CREATED, PROCESSING, COMPLETED, FAILED; }
enum ShipmentType { INCOMING, OUTGOING; }
enum Priority { HIGH, MEDIUM, LOW; }
// Class: Item
class Item {
private final String id;
private final String name;
private final AtomicInteger quantity; // Thread-safe for concurrent updates
public Item(String id, String name, int initialQuantity) {
this.id = id;
this.name = name;
this.quantity = new AtomicInteger(initialQuantity);
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getQuantity() {
return quantity.get();
}
public void increaseQuantity(int amount) {
quantity.addAndGet(amount);
}
public boolean decreaseQuantity(int amount) {
return quantity.addAndGet(-amount) >= 0 || rollbackQuantity(amount);
}
private boolean rollbackQuantity(int amount) {
quantity.addAndGet(amount);
return false;
}
}
// Class: StorageLocation
class StorageLocation {
private final String id;
private final int capacity;
private AtomicInteger currentLoad;
public StorageLocation(String id, int capacity) {
this.id = id;
this.capacity = capacity;
this.currentLoad = new AtomicInteger(0);
}
public boolean canAccommodate(int quantity) {
return (currentLoad.get() + quantity) <= capacity;
}
public void addLoad(int quantity) {
currentLoad.addAndGet(quantity);
}
public void removeLoad(int quantity) {
currentLoad.addAndGet(-quantity);
}
}
// Interface: OptimizationStrategy
interface OptimizationStrategy {
StorageLocation suggestStorageLocation(Item item, int quantity, List<StorageLocation> locations);
}
// Simple Optimization Strategy
class SimpleOptimizationStrategy implements OptimizationStrategy {
@Override
public StorageLocation suggestStorageLocation(Item item, int quantity, List<StorageLocation> locations) {
for (StorageLocation location : locations) {
if (location.canAccommodate(quantity)) {
return location;
}
}
throw new IllegalStateException("No suitable storage location found.");
}
}
// Class: InventoryManager
class InventoryManager {
private final Map<String, Item> inventory = new ConcurrentHashMap<>(); // Thread-safe inventory
public void addItem(String id, String name, int quantity) {
inventory.putIfAbsent(id, new Item(id, name, quantity));
}
public boolean updateStock(String itemId, int quantity) {
Item item = inventory.get(itemId);
if (item == null) return false;
return quantity > 0 ? item.increaseQuantity(quantity) : item.decreaseQuantity(-quantity);
}
public Map<String, Integer> getStockLevels() {
Map<String, Integer> stockLevels = new HashMap<>();
inventory.forEach((id, item) -> stockLevels.put(id, item.getQuantity()));
return stockLevels;
}
}
// Class: OrderManager
class OrderManager {
private final List<Order> orders = Collections.synchronizedList(new ArrayList<>());
public Order createOrder(Map<Item, Integer> items) {
Order order = new Order(UUID.randomUUID().toString(), items);
orders.add(order);
return order;
}
public void processOrder(Order order, InventoryManager inventoryManager) {
synchronized (order) {
for (Map.Entry<Item, Integer> entry : order.getItems().entrySet()) {
Item item = entry.getKey();
int quantity = entry.getValue();
if (!inventoryManager.updateStock(item.getId(), -quantity)) {
order.setStatus(OrderStatus.FAILED);
throw new IllegalStateException("Insufficient stock for item: " + item.getName());
}
}
order.setStatus(OrderStatus.COMPLETED);
}
}
}
// Class: Order
class Order {
private final String id;
private final Map<Item, Integer> items;
private OrderStatus status;
public Order(String id, Map<Item, Integer> items) {
this.id = id;
this.items = items;
this.status = OrderStatus.CREATED;
}
public String getId() {
return id;
}
public Map<Item, Integer> getItems() {
return items;
}
public OrderStatus getStatus() {
return status;
}
public void setStatus(OrderStatus status) {
this.status = status;
}
}
// Class: Warehouse
class Warehouse {
private final String id;
private final List<StorageLocation> locations;
private final InventoryManager inventoryManager;
private final OrderManager orderManager;
private final OptimizationStrategy optimizationStrategy;
public Warehouse(String id, OptimizationStrategy optimizationStrategy) {
this.id = id;
this.locations = Collections.synchronizedList(new ArrayList<>());
this.inventoryManager = new InventoryManager();
this.orderManager = new OrderManager();
this.optimizationStrategy = optimizationStrategy;
}
public void addLocation(StorageLocation location) {
locations.add(location);
}
public void processIncomingShipment(Item item, int quantity) {
StorageLocation location = optimizationStrategy.suggestStorageLocation(item, quantity, locations);
inventoryManager.addItem(item.getId(), item.getName(), quantity);
location.addLoad(quantity);
}
public void processOutgoingOrder(Order order) {
orderManager.processOrder(order, inventoryManager);
}
}
| Single Responsibility | ✅ Yes | Classes have focused responsibilities, reducing reasons for change. |
| Open/Closed | ✅ Yes | The system supports extension via interfaces and modular components without modifying existing code. |
| Liskov Substitution | ✅ Yes | Interfaces and subclasses can replace base types without breaking functionality. |
| Interface Segregation | ✅ Yes | Interfaces are specific and minimal, ensuring classes only implement relevant methods. |
| Dependency Inversion | ✅ Yes | High-level modules rely on abstractions, with interfaces and dependency injection ensuring decoupling. |
Using inheritance for strategy to extend to different storage as required
Try creating a class, flow, state and/or sequence diagram using the diagramming tool. Mermaid flow diagrams can be used to represent system use cases. You can ask the interviewer bot to create a starter diagram if unfamiliar with the tool. Briefly explain your diagrams if necessary...
Critically examine your design for any flaws or areas for future improvement...