LLD System Design

By technoayanβ€’
0views
LLD System Design

A practical guide to leveraging (OOPS) principles in Low-Level Design (LLD). This article explains core OOPS concepts like encapsulation,

🌟 SOLID Principles in Java

The SOLID principles help write clean, organized, and maintainable code. These "rules" prevent confusion and bugs, enabling scalable development.

πŸ”₯ 1. Single Responsibility Principle (SRP)

Rule: A class should have only one responsibility.

πŸ’‘ Example

A Student class should manage only student details, not handle saving data.

β˜•JAVA
// ❌ BAD
class Student {
    void getDetails() { /* Fetch student details */ }
    void saveToFile() { /* Save to file */ } // Unrelated work
}

// βœ… GOOD
class Student {
    void getDetails() { /* Fetch student details */ }
}

class FileManager {
    void saveToFile() { /* Save to file */ }
}

πŸ”₯ 2. Open/Closed Principle (OCP)

Rule: Classes should be open for extension but closed for modification.

πŸ’‘ Example

Adding new shapes without changing existing code:

β˜•JAVA
// ❌ BAD
class AreaCalculator {
    double calculate(String shape, double radius, double length) {
        if (shape.equals("Circle")) return Math.PI * radius * radius;
        else if (shape.equals("Square")) return length * length;
        // Keep adding conditions for new shapes
    }
}

// βœ… GOOD
abstract class Shape {
    abstract double calculateArea();
}

class Circle extends Shape {
    double radius;
    Circle(double radius) { this.radius = radius; }
    double calculateArea() { return Math.PI * radius * radius; }
}

class Square extends Shape {
    double length;
    Square(double length) { this.length = length; }
    double calculateArea() { return length * length; }
}

πŸ”₯ 3. Liskov Substitution Principle (LSP)

Rule: Subclasses should work as substitutes for their parent class without breaking behavior.

πŸ’‘ Example

If Bird is the parent class, subclasses like Sparrow should behave like birds.

β˜•JAVA
// ❌ BAD
class Bird {
    void fly() { System.out.println("Flying"); }
}

class Penguin extends Bird {
    // Penguins don't fly! Violates LSP
}

// βœ… GOOD
abstract class Bird {
    abstract void move();
}

class Sparrow extends Bird {
    void move() { System.out.println("Flying"); }
}

class Penguin extends Bird {
    void move() { System.out.println("Swimming"); }
}

πŸ”₯ 4. Interface Segregation Principle (ISP)

Rule: Don’t force a class to implement methods it doesn't need.

πŸ’‘ Example

β˜•JAVA
// ❌ BAD
interface Animal {
    void fly();
    void swim();
}

class Dog implements Animal {
    public void fly() { /* Empty or throws error */ } // Dogs don't fly
    public void swim() { System.out.println("Dog swimming"); }
}

// βœ… GOOD
interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

class Dog implements Swimmable {
    public void swim() { System.out.println("Dog swimming"); }
}

πŸ”₯ 5. Dependency Inversion Principle (DIP)

Rule: High-level modules should depend on abstractions, not low-level details.

πŸ’‘ Example

β˜•JAVA
// ❌ BAD
class Keyboard { /* Low-level module */ }
class Computer {
    Keyboard keyboard = new Keyboard(); // Hardcoded dependency
}

// βœ… GOOD
interface InputDevice { /* Abstract */ }
class Keyboard implements InputDevice { /* Low-level */ }
class Computer {
    InputDevice device; // Uses abstraction
    Computer(InputDevice device) { this.device = device; }
}

🎯 Why Use These Principles?

  • πŸ› οΈ Cleaner code that's easier to understand.
  • πŸ” Reduces bugs and unexpected behavior.
  • πŸš€ Simplifies adding features without breaking existing code.

πŸ“š Library Management System (LLD Example in Java)

This system demonstrates the SOLID principles in action to design a scalable and maintainable Library Management System.


🎯 Scenario

The Library Management System allows users to:

  • πŸ” Search for books by title or author.
  • πŸ“– Borrow and return books.
  • πŸ’Ύ Maintain user and book data.

πŸ’‘ Code Implementation

πŸ”Έ 1. Book Class

The Book class represents a book in the library. It maintains its data (e.g., title, author, availability) and provides methods to borrow or return the book.

β˜•JAVA
class Book {
    private String id;
    private String title;
    private String author;
    private boolean isAvailable;

    public Book(String id, String title, String author) {
        this.id = id;
        this.title = title;
        this.author = author;
        this.isAvailable = true;
    }

    public String getId() { return id; }
    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public boolean isAvailable() { return isAvailable; }

    public void borrowBook() { this.isAvailable = false; }
    public void returnBook() { this.isAvailable = true; }
}

πŸ”Έ 2. User Class

The User class represents a library user with basic details like userId and name.

β˜•JAVA
class User {
    private String userId;
    private String name;

    public User(String userId, String name) {
        this.userId = userId;
        this.name = name;
    }

    public String getUserId() { return userId; }
    public String getName() { return name; }
}

πŸ”Έ 3. Library Class

The Library class manages the collection of books and provides functionality for searching, borrowing, and returning books.

β˜•JAVA
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

class Library {
    private List<Book> books;

    public Library() {
        books = new ArrayList<>();
    }

    public void addBook(Book book) {
        books.add(book);
    }

    public List<Book> searchByTitle(String title) {
        return books.stream()
                .filter(book -> book.getTitle().equalsIgnoreCase(title))
                .collect(Collectors.toList());
    }

    public List<Book> searchByAuthor(String author) {
        return books.stream()
                .filter(book -> book.getAuthor().equalsIgnoreCase(author))
                .collect(Collectors.toList());
    }

    public boolean borrowBook(String bookId, User user) {
        for (Book book : books) {
            if (book.getId().equals(bookId) && book.isAvailable()) {
                book.borrowBook();
                System.out.println(user.getName() + " borrowed " + book.getTitle());
                return true;
            }
        }
        System.out.println("Book not available for borrowing.");
        return false;
    }

    public void returnBook(String bookId, User user) {
        for (Book book : books) {
            if (book.getId().equals(bookId) && !book.isAvailable()) {
                book.returnBook();
                System.out.println(user.getName() + " returned " + book.getTitle());
                return;
            }
        }
        System.out.println("Invalid return attempt.");
    }
}

πŸ”Έ 4. Main Class

The LibraryManagementSystem class tests the functionality of the library system.

β˜•JAVA
public class LibraryManagementSystem {
    public static void main(String[] args) {
        // Create Library
        Library library = new Library();

        // Add Books
        library.addBook(new Book("1", "Java Basics", "John Doe"));
        library.addBook(new Book("2", "Python Guide", "Jane Smith"));
        library.addBook(new Book("3", "Data Structures", "John Doe"));

        // Create User
        User user = new User("101", "Alice");

        // Search by Title
        System.out.println("Search Results for 'Java Basics':");
        for (Book book : library.searchByTitle("Java Basics")) {
            System.out.println(book.getTitle() + " by " + book.getAuthor());
        }

        // Borrow Book
        library.borrowBook("1", user);

        // Try to Borrow the Same Book Again
        library.borrowBook("1", user);

        // Return Book
        library.returnBook("1", user);

        // Try to Return the Same Book Again
        library.returnBook("1", user);
    }
}

πŸ› οΈ Output

Search Results for 'Java Basics': Java Basics by John Doe Alice borrowed Java Basics Book not available for borrowing. Alice returned Java Basics Invalid return attempt.

πŸ“Œ SOLID Principles Applied

  • SRP: Each class handles a single responsibility: Book for book details, User for user information, and Library for operations.
  • OCP: Easily extendable (e.g., adding genres or additional features).
  • LSP: Subclasses (if introduced) will follow their parent class without breaking behavior.
  • ISP: Interfaces can be introduced for specific types of books (e.g., EBooks, AudioBooks).
  • DIP: Library interacts with Book and User via abstraction methods, avoiding hard dependencies.

πŸš— Parking Lot System (LLD Example in Java)

This system demonstrates a scalable, thread-safe, and maintainable Parking Lot design based on SOLID principles.


🎯 Requirements

  1. 🏒 Multiple levels, each with a set number of spots.
  2. πŸš— Support for different vehicle types: cars, motorcycles, trucks.
  3. πŸ“ Assign and release parking spots dynamically.
  4. πŸ”„ Track real-time availability of parking spots.
  5. πŸ…ΏοΈ Handle multiple entry and exit points with concurrency.

πŸ’‘ Code Implementation

πŸ”Έ 1. Enumerations

β˜•JAVA
enum VehicleType {
    CAR, MOTORCYCLE, TRUCK
}

πŸ”Έ 2. Vehicle Class Hierarchy

Abstract Class: Vehicle

β˜•JAVA
abstract class Vehicle {
    private String licensePlate;
    private VehicleType type;

    public Vehicle(String licensePlate, VehicleType type) {
        this.licensePlate = licensePlate;
        this.type = type;
    }

    public String getLicensePlate() { return licensePlate; }
    public VehicleType getType() { return type; }
}

Subclasses: Car, Motorcycle, Truck

β˜•JAVA
class Car extends Vehicle {
    public Car(String licensePlate) {
        super(licensePlate, VehicleType.CAR);
    }
}

class Motorcycle extends Vehicle {
    public Motorcycle(String licensePlate) {
        super(licensePlate, VehicleType.MOTORCYCLE);
    }
}

class Truck extends Vehicle {
    public Truck(String licensePlate) {
        super(licensePlate, VehicleType.TRUCK);
    }
}

πŸ”Έ 3. ParkingSpot Class

Represents a parking spot and ensures thread safety.

β˜•JAVA
class ParkingSpot {
    private VehicleType spotType;
    private boolean isAvailable;
    private Vehicle parkedVehicle;

    public ParkingSpot(VehicleType spotType) {
        this.spotType = spotType;
        this.isAvailable = true;
    }

    public synchronized boolean parkVehicle(Vehicle vehicle) {
        if (isAvailable && vehicle.getType() == spotType) {
            this.parkedVehicle = vehicle;
            this.isAvailable = false;
            return true;
        }
        return false;
    }

    public synchronized void removeVehicle() {
        this.parkedVehicle = null;
        this.isAvailable = true;
    }

    public boolean isAvailable() { return isAvailable; }
    public Vehicle getParkedVehicle() { return parkedVehicle; }
}

πŸ”Έ 4. Level Class

Handles parking and releasing vehicles at a specific level.

β˜•JAVA
import java.util.ArrayList;
import java.util.List;

class Level {
    private int levelNumber;
    private List<ParkingSpot> spots;

    public Level(int levelNumber, int numSpots, VehicleType spotType) {
        this.levelNumber = levelNumber;
        this.spots = new ArrayList<>();
        for (int i = 0; i < numSpots; i++) {
            spots.add(new ParkingSpot(spotType));
        }
    }

    public boolean parkVehicle(Vehicle vehicle) {
        for (ParkingSpot spot : spots) {
            if (spot.parkVehicle(vehicle)) {
                System.out.println("Vehicle parked at level " + levelNumber);
                return true;
            }
        }
        return false;
    }

    public void releaseVehicle(String licensePlate) {
        for (ParkingSpot spot : spots) {
            if (spot.getParkedVehicle() != null &&
                spot.getParkedVehicle().getLicensePlate().equals(licensePlate)) {
                spot.removeVehicle();
                System.out.println("Vehicle removed from level " + levelNumber);
                return;
            }
        }
        System.out.println("Vehicle not found in level " + levelNumber);
    }

    public int getAvailableSpots() {
        return (int) spots.stream().filter(ParkingSpot::isAvailable).count();
    }
}

πŸ”Έ 5. ParkingLot Class (Singleton)

Represents the entire parking lot and ensures only one instance exists.

β˜•JAVA
import java.util.ArrayList;
import java.util.List;

class ParkingLot {
    private static ParkingLot instance;
    private List<Level> levels;

    private ParkingLot() {
        levels = new ArrayList<>();
    }

    public static synchronized ParkingLot getInstance() {
        if (instance == null) {
            instance = new ParkingLot();
        }
        return instance;
    }

    public void addLevel(Level level) {
        levels.add(level);
    }

    public boolean parkVehicle(Vehicle vehicle) {
        for (Level level : levels) {
            if (level.parkVehicle(vehicle)) {
                return true;
            }
        }
        System.out.println("No parking spot available for vehicle: " + vehicle.getLicensePlate());
        return false;
    }

    public void releaseVehicle(String licensePlate) {
        for (Level level : levels) {
            level.releaseVehicle(licensePlate);
        }
    }

    public void displayAvailableSpots() {
        for (int i = 0; i < levels.size(); i++) {
            System.out.println("Level " + i + ": " + levels.get(i).getAvailableSpots() + " spots available.");
        }
    }
}

πŸ”Έ 6. Main Class

Demonstrates the usage of the parking lot system.

β˜•JAVA
public class Main {
    public static void main(String[] args) {
        // Initialize Parking Lot
        ParkingLot parkingLot = ParkingLot.getInstance();
        parkingLot.addLevel(new Level(0, 2, VehicleType.CAR));
        parkingLot.addLevel(new Level(1, 3, VehicleType.MOTORCYCLE));
        parkingLot.addLevel(new Level(2, 1, VehicleType.TRUCK));

        // Create Vehicles
        Vehicle car1 = new Car("CAR123");
        Vehicle bike1 = new Motorcycle("BIKE123");
        Vehicle truck1 = new Truck("TRUCK123");

        // Park Vehicles
        parkingLot.parkVehicle(car1);
        parkingLot.parkVehicle(bike1);
        parkingLot.parkVehicle(truck1);

        // Display Available Spots
        parkingLot.displayAvailableSpots();

        // Release Vehicles
        parkingLot.releaseVehicle("CAR123");
        parkingLot.releaseVehicle("BIKE123");

        // Display Available Spots Again
        parkingLot.displayAvailableSpots();
    }
}

πŸ› οΈ Output

Vehicle parked at level 0 Vehicle parked at level 1 Vehicle parked at level 2 Level 0: 1 spots available. Level 1: 2 spots available. Level 2: 0 spots available. Vehicle removed from level 0 Vehicle removed from level 1 Level 0: 2 spots available. Level 1: 3 spots available. Level 2: 0 spots available.

πŸ“Œ Key Features

  • Thread-Safety: Ensured using synchronized keyword for critical sections.
  • Singleton Pattern: Maintains a single ParkingLot instance.
  • Modular Design: Each class handles one responsibility (SRP).
  • Scalability: Easily add new levels or support additional vehicle types.

Thanks for reading!

technoayan

Author & Tech Enthusiast

"Keep learning, keep growing, and keep sharing knowledge with the world."

Show Some Love

Let the author know you enjoyed this content

0
people like this

Sign in to show your appreciation

Share This Post

Help others discover this content

Rate This Post

Share your thoughts and help others discover great content

Sign in to rate this post and share your feedback

Community Rating

No ratings yet. Be the first to rate this post!

More in LLD

Comments (0)

Leave a Comment

No comments yet. Be the first to share your thoughts!