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 |
|-------------------|
| - inventoryMgr |--------> (uses) --------+
| - orderMgr | |
| - locationMgr | v
|-------------------| +-----------------------+
| + receiveShipment() | InventoryManager |
| + shipOrder(orderId) |-----------------------|
| + optimizeLocations() | - stock: Map
| + placeOrder(...) |-----------------------|
| + ... | + addStock(item, qty)|
+-------------------+ | + removeStock(...) |
| + getStockLevel(...) |
+-----------------------+
^
|
+------------------------+ |
| OrderManager |<-----------------------+
|------------------------| (uses)
| - orders: Map
|------------------------|------------------------> Order (entity) |
| + createOrder(...) | |--------------------|
| + getOrder(...) | | - id: UUID |
| + updateOrder(...) | | - items: List
| + fulfillOrder(...) | | - status: OrderStatus
+------------------------+ +--------------------+
+-------------------------+
| LocationManager |
|-------------------------|
| - strategy: LocationStrategy
| - locations: List
|-------------------------|
| + optimizeLocations() |
| + addLocation(loc) |
| + removeLocation(loc) |
+----------^--------------+
|
|
+----------------------+
| LocationStrategy |
|----------------------|
| + optimize(...) |
+----------------------+
/\
/ \
----
... (Multiple strategy implementations)
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.public class Item {
private final String name;
private final String sku;
public Item(String name, String sku) {
this.name = name;
this.sku = sku;
}
public String getName() {
return name;
}
public String getSku() {
return sku;
}
// equals, hashCode, toString ...
}
public enum OrderStatus {
CREATED, PROCESSING, SHIPPED, COMPLETED, CANCELLED
}
public class Order {
private final UUID id;
private final List<Item> items;
private OrderStatus status;
public Order(List<Item> items) {
this.id = UUID.randomUUID();
this.items = new ArrayList<>(items);
this.status = OrderStatus.CREATED;
}
public UUID getId() {
return id;
}
public List<Item> getItems() {
return items;
}
public OrderStatus getStatus() {
return status;
}
public void setStatus(OrderStatus status) {
this.status = status;
}
}
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class InventoryManager {
// Key: Item, Value: quantity in stock
private final ConcurrentMap<Item, Integer> stock;
public InventoryManager() {
this.stock = new ConcurrentHashMap<>();
}
public void addStock(Item item, int quantity) {
stock.merge(item, quantity, Integer::sum);
}
public boolean removeStock(Item item, int quantity) {
return stock.computeIfPresent(item, (k, oldQty) -> {
if (oldQty >= quantity) {
return oldQty - quantity;
}
// Not enough stock
return oldQty;
}) >= 0; // if result is null or negative, handle accordingly
}
public int getStockLevel(Item item) {
return stock.getOrDefault(item, 0);
}
// Additional methods if needed...
}
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class OrderManager {
private final Map<UUID, Order> orders;
public OrderManager() {
this.orders = new ConcurrentHashMap<>();
}
public Order createOrder(List<Item> items) {
Order order = new Order(items);
orders.put(order.getId(), order);
return order;
}
public Order getOrder(UUID orderId) {
return orders.get(orderId);
}
public void updateOrder(Order order) {
orders.put(order.getId(), order);
}
public void fulfillOrder(UUID orderId, InventoryManager inventoryManager) {
Order order = orders.get(orderId);
if (order != null && order.getStatus() == OrderStatus.CREATED) {
// Attempt to remove stock
for (Item item : order.getItems()) {
// In a real system, we'd check quantity needed for each item
if (inventoryManager.getStockLevel(item) <= 0) {
throw new RuntimeException("Insufficient stock for item: " + item.getName());
}
}
// If all checks pass, remove stock
for (Item item : order.getItems()) {
inventoryManager.removeStock(item, 1); // Example: removing 1 for each item
}
order.setStatus(OrderStatus.PROCESSING);
orders.put(orderId, order);
}
}
}
public class Location {
private final String locationId;
private final int capacity; // e.g., how many items can fit
// Possibly store some metadata: zone, aisle, etc.
public Location(String locationId, int capacity) {
this.locationId = locationId;
this.capacity = capacity;
}
public String getLocationId() {
return locationId;
}
public int getCapacity() {
return capacity;
}
}
public interface LocationStrategy {
void optimize(List<Location> locations);
}
public class SimpleLocationStrategy implements LocationStrategy {
@Override
public void optimize(List<Location> locations) {
// A simple example: sort by capacity, maybe group items accordingly
// This is just a placeholder to demonstrate the strategy pattern
locations.sort((a, b) -> Integer.compare(a.getCapacity(), b.getCapacity()));
}
}
import java.util.List;
public class LocationManager {
private final List<Location> locations;
private LocationStrategy strategy;
public LocationManager(LocationStrategy strategy) {
this.strategy = strategy;
this.locations = new ArrayList<>();
}
public void addLocation(Location location) {
locations.add(location);
}
public void removeLocation(Location location) {
locations.remove(location);
}
public void optimizeLocations() {
strategy.optimize(locations);
}
public void setStrategy(LocationStrategy strategy) {
this.strategy = strategy;
}
public List<Location> getLocations() {
return List.copyOf(locations);
}
}
public abstract class Shipment {
private final UUID shipmentId;
private final List<Item> items;
public Shipment(List<Item> items) {
this.shipmentId = UUID.randomUUID();
this.items = new ArrayList<>(items);
}
public UUID getShipmentId() {
return shipmentId;
}
public List<Item> getItems() {
return items;
}
public abstract void process(InventoryManager inventoryManager);
}
public class InboundShipment extends Shipment {
public InboundShipment(List<Item> items) {
super(items);
}
@Override
public void process(InventoryManager inventoryManager) {
// For each item in the shipment, add to inventory
for (Item item : getItems()) {
inventoryManager.addStock(item, 1); // Example quantity = 1
}
}
}
public class OutboundShipment extends Shipment {
public OutboundShipment(List<Item> items) {
super(items);
}
@Override
public void process(InventoryManager inventoryManager) {
// For each item, remove from inventory
for (Item item : getItems()) {
inventoryManager.removeStock(item, 1);
}
}
}
public class Warehouse {
private final InventoryManager inventoryManager;
private final OrderManager orderManager;
private final LocationManager locationManager;
public Warehouse(LocationStrategy locationStrategy) {
this.inventoryManager = new InventoryManager();
this.orderManager = new OrderManager();
this.locationManager = new LocationManager(locationStrategy);
}
public void receiveShipment(Shipment shipment) {
// Typically InboundShipment
shipment.process(inventoryManager);
}
public void shipOrder(UUID orderId) {
Order order = orderManager.getOrder(orderId);
if (order != null && order.getStatus() == OrderStatus.PROCESSING) {
// Mark as shipped, possibly create an OutboundShipment
OutboundShipment shipment = new OutboundShipment(order.getItems());
shipment.process(inventoryManager);
order.setStatus(OrderStatus.SHIPPED);
orderManager.updateOrder(order);
}
}
public Order placeOrder(List<Item> items) {
return orderManager.createOrder(items);
}
public void fulfillOrder(UUID orderId) {
orderManager.fulfillOrder(orderId, inventoryManager);
}
public void optimizeLocations() {
locationManager.optimizeLocations();
}
// Accessors if needed
public InventoryManager getInventoryManager() {
return inventoryManager;
}
public OrderManager getOrderManager() {
return orderManager;
}
public LocationManager getLocationManager() {
return locationManager;
}
}
| 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...