Designing an Inventory Management System

Ashutosh Kumar Singh
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

  1. 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

--

--

Ashutosh Kumar Singh
Ashutosh Kumar Singh

No responses yet