OOD / Allocation• Patterns: Strategy, Factory, Observer
Amazon Locker
Medium
Problem Summary
Design an automated Amazon Locker network managing package dimensions, optimal locker compartment assignment, one-time secure pickup codes, and door sensor telemetry.
Functional Scope
- Physical compartments of varied sizes: Small, Medium, Large, Extra Large.
- Assign best-fit locker compartment to packages using a pluggable AllocationStrategy.
- Generate cryptographically secure 6-digit PIN and QR pickup token.
- Deposit flow by courier: opens assigned door and locks upon closure.
- Customer pickup flow: validates OTP, opens door, and notifies logistics system.
Entity-Relationship (ER) Schema
LockerHub [1] --> [*] LockerCompartment LockerHub [1] --> [1] AllocationStrategy Package [1] --> [0..1] LockerCompartment PickupTicket [1] --> [1] Package LockerHub [1] --> [*] LockerObserver
Design Approach
Model compartment dimensions and apply the Best-Fit Strategy pattern to minimize space wastage. Send customer notifications through the Observer pattern upon courier closure confirmation.
Core Classes & Models
LockerHub (Physical locker installation context)LockerCompartment (SlotId, CompartmentSize, CompartmentState: EMPTY, OCCUPIED, JAMMED)Package (Dimensions, tracking number)AllocationStrategy (BestFitStrategy, EnergyClusteringStrategy)PickupTicket (6-digit OTP, expiration timestamp)LockerObserver (Notifies customer and logistics cloud)
Code Blueprint
110 lines/**
* Low-Level Design: Amazon Locker Delivery System
* Design Patterns Applied:
* - Strategy Pattern: Locker slot allocation strategy (Best-fit size matching)
* - Factory Pattern: Package and locker compartment generation
* - Observer Pattern: Real-time customer OTP notification and pickup alerts
*/
import java.util.*;
enum LockerSize { SMALL, MEDIUM, LARGE }
// --- [DESIGN PATTERN: Observer] Notification listener for OTP ---
interface CustomerNotifier {
void onPackageDelivered(String customerEmail, String lockerId, String otpCode);
}
class EmailCustomerNotifier implements CustomerNotifier {
@Override
public void onPackageDelivered(String customerEmail, String lockerId, String otpCode) {
System.out.println("📧 [Email sent to " + customerEmail + "] Your package is in Locker ["
+ lockerId + "]. Secret OTP: " + otpCode);
}
}
class LockerCompartment {
private final String id;
private final LockerSize size;
private boolean occupied = false;
private String currentOtp = null;
public LockerCompartment(String id, LockerSize size) { this.id = id; this.size = size; }
public String getId() { return id; }
public LockerSize getSize() { return size; }
public boolean isOccupied() { return occupied; }
public void deposit(String otp) { this.occupied = true; this.currentOtp = otp; }
public boolean verifyAndOpen(String otp) {
if (occupied && currentOtp != null && currentOtp.equals(otp)) {
this.occupied = false;
this.currentOtp = null;
return true;
}
return false;
}
}
// --- [DESIGN PATTERN: Strategy] Locker allocation strategy ---
interface LockerAllocationStrategy {
LockerCompartment findLocker(List<LockerCompartment> compartments, LockerSize requiredSize);
}
class BestFitAllocationStrategy implements LockerAllocationStrategy {
@Override
public LockerCompartment findLocker(List<LockerCompartment> compartments, LockerSize requiredSize) {
for (LockerCompartment comp : compartments) {
if (!comp.isOccupied() && comp.getSize().ordinal() >= requiredSize.ordinal()) {
return comp;
}
}
return null;
}
}
class AmazonLockerService {
private final List<LockerCompartment> compartments = new ArrayList<>();
private final LockerAllocationStrategy allocationStrategy = new BestFitAllocationStrategy();
private final List<CustomerNotifier> notifiers = new ArrayList<>();
public void addCompartment(LockerCompartment comp) { compartments.add(comp); }
public void addNotifier(CustomerNotifier n) { notifiers.add(n); }
public synchronized boolean depositPackage(String customerEmail, LockerSize size) {
LockerCompartment slot = allocationStrategy.findLocker(compartments, size);
if (slot == null) {
System.out.println("❌ No available locker compartment for size: " + size);
return false;
}
String otp = String.format("%06d", new Random().nextInt(999999));
slot.deposit(otp);
for (CustomerNotifier n : notifiers) n.onPackageDelivered(customerEmail, slot.getId(), otp);
return true;
}
public synchronized boolean pickupPackage(String slotId, String otp) {
for (LockerCompartment slot : compartments) {
if (slot.getId().equals(slotId)) {
if (slot.verifyAndOpen(otp)) {
System.out.println("🔓 Locker [" + slotId + "] unlocked! Package retrieved.");
return true;
}
}
}
System.out.println("❌ Invalid OTP for Locker [" + slotId + "]!");
return false;
}
}
public class Main {
public static void main(String[] args) {
AmazonLockerService lockerHub = new AmazonLockerService();
lockerHub.addNotifier(new EmailCustomerNotifier());
lockerHub.addCompartment(new LockerCompartment("LC-01", LockerSize.SMALL));
lockerHub.addCompartment(new LockerCompartment("LC-02", LockerSize.LARGE));
System.out.println("--- Amazon Locker Simulation ---");
lockerHub.depositPackage("john.doe@example.com", LockerSize.MEDIUM);
}
}