Designing an Inventory Management System
4 min readSep 21, 2024
In this design, we’ll implement the Inventory Management System using various design patterns like Singleton, Factory, Strategy, and Observer. We’ll also provide a class diagram, database schema, and Java code for implementation.
Step 1: Design Patterns Used
- Singleton Pattern:
- Used for the
Inventory
class to ensure only one instance of the inventory system exists.
2. Factory Pattern:
- Used to create different types of products (e.g., electronics, clothing, groceries).
3. Observer Pattern:
- Used to notify subscribers (e.g., suppliers) when stock goes below a certain threshold.
4. Strategy Pattern:
- Used to dynamically change the stock replenishment strategy.
Step 2: Class Diagram
+----------------------------------+
| Inventory | (Singleton)
+----------------------------------+
| - products: List<Product> |
| - observers: List<Observer> |
+----------------------------------+
| + getInstance(): Inventory |
| + addProduct(product: Product): void |
| + updateProduct(productId: int, qty: int): void |
| + removeProduct(productId: int): void |
| + checkStock(productId: int): int |
| + notifyObservers(productId: int): void |
| + addObserver(observer: Observer): void |
+----------------------------------+
+----------------------------------+
| ProductFactory | (Factory)
+----------------------------------+
| + createProduct(type: String): Product |
+----------------------------------++----------------------------------+
| Product | (Abstract)
+----------------------------------+
| - id: int |
| - name: String |
| - price: float |
| - quantity: int |
+----------------------------------+
| + getId(): int |
| + getName(): String |
| + getQuantity(): int |
| + setQuantity(qty: int): void |
| + restock(qty: int): void |
+----------------------------------++----------------------------+
| ElectronicsProduct | (Concrete)
+----------------------------+
| - warrantyPeriod: int |
+----------------------------+
| + getWarrantyPeriod(): int |
+----------------------------++----------------------------+
| GroceryProduct | (Concrete)
+----------------------------+
| - expiryDate: Date |
+----------------------------+
| + getExpiryDate(): Date |
+----------------------------++----------------------------+
| ClothingProduct | (Concrete)
+----------------------------+
| - size: String |
| - material: String |
+----------------------------+
| + getSize(): String |
| + getMaterial(): String |
+----------------------------++----------------------------------+
| StockReplenisher | (Strategy)
+----------------------------------+
| + replenish(product: Product): void |
+----------------------------------++----------------------------------+
| ThresholdReplenisher | (Concrete)
+----------------------------------+
| + replenish(product: Product): void |
+----------------------------------++----------------------------------+
| DemandBasedReplenisher | (Concrete)
+----------------------------------+
| + replenish(product: Product): void |
+----------------------------------++----------------------------------+
| Observer | (Abstract)
+----------------------------------+
| + update(productId: int): void |
+----------------------------------++----------------------------------+
| Supplier | (Concrete)
+----------------------------------+
| + update(productId: int): void |
+----------------------------------+
Step 3: Database Schema
Tables:
Step 4: Java Code Implementation
Inventory (Singleton)
public class Inventory {
private static Inventory instance;
private List<Product> products = new ArrayList<>();
private List<Observer> observers = new ArrayList<>();
private Inventory() {}
public static synchronized Inventory getInstance() {
if (instance == null) {
instance = new Inventory();
}
return instance;
} public void addProduct(Product product) {
products.add(product);
} public void updateProduct(int productId, int quantity) {
for (Product product : products) {
if (product.getId() == productId) {
product.setQuantity(quantity);
notifyObservers(productId);
}
}
} public void removeProduct(int productId) {
products.removeIf(product -> product.getId() == productId);
} public void checkStock(int productId) {
for (Product product : products) {
if (product.getId() == productId) {
System.out.println("Stock level for product: " + product.getQuantity());
}
}
} public void addObserver(Observer observer) {
observers.add(observer);
} private void notifyObservers(int productId) {
for (Observer observer : observers) {
observer.update(productId);
}
}
}
Product and Product Types
public abstract class Product {
protected int id;
protected String name;
protected float price;
protected int quantity;
public int getId() { return id; }
public String getName() { return name; }
public int getQuantity() { return quantity; } public void setQuantity(int qty) { this.quantity = qty; }
}public class ElectronicsProduct extends Product {
private int warrantyPeriod; public ElectronicsProduct(int id, String name, float price, int warrantyPeriod) {
this.id = id;
this.name = name;
this.price = price;
this.warrantyPeriod = warrantyPeriod;
} public int getWarrantyPeriod() {
return warrantyPeriod;
}
}public class GroceryProduct extends Product {
private Date expiryDate; public GroceryProduct(int id, String name, float price, Date expiryDate) {
this.id = id;
this.name = name;
this.price = price;
this.expiryDate = expiryDate;
} public Date getExpiryDate() {
return expiryDate;
}
}public class ClothingProduct extends Product {
private String size;
private String material; public ClothingProduct(int id, String name, float price, String size, String material) {
this.id = id;
this.name = name;
this.price = price;
this.size = size;
this.material = material;
} public String getSize() {
return size;
} public String getMaterial() {
return material;
}
}
ProductFactory (Factory Pattern)
public class ProductFactory {
public static Product createProduct(String type, int id, String name, float price) {
switch (type.toLowerCase()) {
case "electronics":
return new ElectronicsProduct(id, name, price, 12); // 12 months warranty
case "grocery":
return new GroceryProduct(id, name, price, new Date()); // today's date for now
case "clothing":
return new ClothingProduct(id, name, price, "M", "Cotton");
default:
throw new IllegalArgumentException("Unknown product type");
}
}
}
Observer Pattern
public interface Observer {
void update(int productId);
}
public class Supplier implements Observer {
@Override
public void update(int productId) {
System.out.println("Supplier notified for product ID: " + productId);
}
}
Strategy Pattern
public interface StockReplenisher {
void replenish(Product product);
}
public class ThresholdReplenisher implements StockReplenisher {
@Override
public void replenish(Product product) {
if (product.getQuantity() < 10) {
System.out.println("Replenishing stock for product: " + product.getName());
}
}
}public class DemandBasedReplenisher implements StockReplenisher {
@Override
public void replenish(Product product) {
System.out.println("Demand-based replenishment for product: " + product.getName());
}
}
This design provides a solid foundation for an Inventory Management System that is scalable and flexible with the use of design patterns like Singleton, Factory, Strategy