📂
Interview Prep
LLD Questions
LLD Questions
  • LLD Introduction
  • Meeting Scheduler
  • Distributed Cache System
  • Rate limiter
  • Multi-Tier Elevator System
  • MyGate System
  • Uber Sytem LLD
  • Parking Lot System
  • Online book store
  • Library Management System
  • Movie Ticket Booking System
  • Hotel Management System
  • File Storage System
    • Solution 2
  • Chat Application
  • Social Media Platform
  • Notification System
  • Airline Reservation System
  • ATM System
  • E-commerce Website
  • Food Delivery System
  • Shopping Cart System
  • URL Shortener system
  • Chess Tournament System
  • Threading therory
  • OOP questions
Powered by GitBook
On this page

Multi-Tier Elevator System

Multi-Tier Elevator System: Low-Level Design (LLD) with Java Code and Design Patterns

In this answer, we will design a Multi-Tier Elevator System for a building with multiple floors and several elevators. The design will include various OOD patterns such as Strategy Pattern for elevator movement, Command Pattern for processing requests, and Factory Pattern for creating elevator instances. Additionally, we'll discuss algorithms like FIFO (First-In, First-Out), SCAN, and Priority Weight for elevator request scheduling.


Design Overview

In a Multi-Tier Elevator System, we need to manage elevators that move between floors based on requests from passengers. The system should be capable of managing multiple elevators and handling requests efficiently while ensuring that requests are processed in an optimized way.

Key components of the system:

  1. Elevator: Represents an elevator that moves between floors.

  2. Floor: Represents a floor in the building.

  3. Request: Represents a request made by a passenger to either go up or down to a specific floor.

  4. ElevatorControlSystem: Manages multiple elevators and processes requests.

  5. Scheduler: Schedules the request processing using different algorithms.

  6. MovementStrategy: Provides different movement strategies for elevators.

  7. Command: Represents an action to be performed on the elevator system (e.g., request handling).

We will use the following design patterns:

  • Strategy Pattern for elevator movement algorithms.

  • Command Pattern for processing requests.

  • Factory Pattern for creating different types of elevator instances.

  • Observer Pattern for notifying passengers when their requested elevator arrives.


Classes and Data Structures

1. Elevator Class

The Elevator class represents the elevator that moves between floors.

class Elevator {
    private int currentFloor;
    private boolean isMoving;
    private Direction direction;
    private final int id;

    public enum Direction {
        UP, DOWN, NONE
    }

    public Elevator(int id) {
        this.currentFloor = 0; // Ground floor
        this.isMoving = false;
        this.direction = Direction.NONE;
        this.id = id;
    }

    public int getCurrentFloor() {
        return currentFloor;
    }

    public void moveTo(int floor) {
        this.isMoving = true;
        if (currentFloor < floor) {
            direction = Direction.UP;
        } else {
            direction = Direction.DOWN;
        }
        // Simulate movement by changing the floor number.
        this.currentFloor = floor;
        this.isMoving = false;
        System.out.println("Elevator " + id + " arrived at floor " + floor);
    }

    public Direction getDirection() {
        return direction;
    }
    
    public boolean isMoving() {
        return isMoving;
    }
}

2. Request Class

The Request class represents a request to move an elevator to a specific floor.

class Request {
    private int targetFloor;
    private int requestTime; // Time when the request was made (used for FIFO algorithm).

    public Request(int targetFloor, int requestTime) {
        this.targetFloor = targetFloor;
        this.requestTime = requestTime;
    }

    public int getTargetFloor() {
        return targetFloor;
    }

    public int getRequestTime() {
        return requestTime;
    }
}

3. Scheduler Class (Strategy Pattern)

The Scheduler class decides which algorithm to use for processing elevator requests. We will use a Strategy Pattern to allow dynamic selection of different algorithms (FIFO, SCAN, Priority-based).

interface RequestSchedulingStrategy {
    void scheduleRequest(List<Request> requests, List<Elevator> elevators);
}

class FIFOScheduler implements RequestSchedulingStrategy {
    @Override
    public void scheduleRequest(List<Request> requests, List<Elevator> elevators) {
        requests.sort(Comparator.comparingInt(Request::getRequestTime)); // FIFO by request time
        processRequests(requests, elevators);
    }

    private void processRequests(List<Request> requests, List<Elevator> elevators) {
        for (Request request : requests) {
            Elevator selectedElevator = selectElevatorForRequest(request, elevators);
            selectedElevator.moveTo(request.getTargetFloor());
        }
    }

    private Elevator selectElevatorForRequest(Request request, List<Elevator> elevators) {
        // Simple heuristic: Choose the closest elevator
        return elevators.stream().min(Comparator.comparingInt(elevator -> Math.abs(elevator.getCurrentFloor() - request.getTargetFloor()))).get();
    }
}

class SCANScheduler implements RequestSchedulingStrategy {
    @Override
    public void scheduleRequest(List<Request> requests, List<Elevator> elevators) {
        // Sort requests by floor, and process in SCAN order
        requests.sort(Comparator.comparingInt(Request::getTargetFloor));
        processRequests(requests, elevators);
    }

    private void processRequests(List<Request> requests, List<Elevator> elevators) {
        for (Request request : requests) {
            Elevator selectedElevator = selectElevatorForRequest(request, elevators);
            selectedElevator.moveTo(request.getTargetFloor());
        }
    }

    private Elevator selectElevatorForRequest(Request request, List<Elevator> elevators) {
        // Simple heuristic: Choose the elevator closest to the requested floor
        return elevators.stream().min(Comparator.comparingInt(elevator -> Math.abs(elevator.getCurrentFloor() - request.getTargetFloor()))).get();
    }
}

class PriorityScheduler implements RequestSchedulingStrategy {
    @Override
    public void scheduleRequest(List<Request> requests, List<Elevator> elevators) {
        // Sort requests by priority (higher priority for lower floors)
        requests.sort(Comparator.comparingInt(Request::getTargetFloor));
        processRequests(requests, elevators);
    }

    private void processRequests(List<Request> requests, List<Elevator> elevators) {
        for (Request request : requests) {
            Elevator selectedElevator = selectElevatorForRequest(request, elevators);
            selectedElevator.moveTo(request.getTargetFloor());
        }
    }

    private Elevator selectElevatorForRequest(Request request, List<Elevator> elevators) {
        // Simple heuristic: Choose the closest elevator
        return elevators.stream().min(Comparator.comparingInt(elevator -> Math.abs(elevator.getCurrentFloor() - request.getTargetFloor()))).get();
    }
}

class ElevatorControlSystem {
    private List<Elevator> elevators;
    private List<Request> requestQueue;
    private RequestSchedulingStrategy schedulingStrategy;

    public ElevatorControlSystem(int numElevators, RequestSchedulingStrategy schedulingStrategy) {
        this.elevators = new ArrayList<>();
        for (int i = 0; i < numElevators; i++) {
            elevators.add(new Elevator(i + 1));
        }
        this.requestQueue = new ArrayList<>();
        this.schedulingStrategy = schedulingStrategy;
    }

    public void addRequest(Request request) {
        requestQueue.add(request);
    }

    public void processRequests() {
        schedulingStrategy.scheduleRequest(requestQueue, elevators);
    }
}

4. Command Pattern for Request Processing

The Command Pattern is used for encapsulating requests as command objects. This allows for flexibility and decouples the request generation from the execution of the request.

interface Command {
    void execute();
}

class RequestCommand implements Command {
    private ElevatorControlSystem system;
    private Request request;

    public RequestCommand(ElevatorControlSystem system, Request request) {
        this.system = system;
        this.request = request;
    }

    @Override
    public void execute() {
        system.addRequest(request);
    }
}

Main Class to Demonstrate the System

import java.util.*;

public class Main {
    public static void main(String[] args) {
        // Create a system with 3 elevators, using the FIFO scheduling strategy
        ElevatorControlSystem elevatorSystem = new ElevatorControlSystem(3, new FIFOScheduler());

        // Simulate requests (with time as part of FIFO)
        Request request1 = new Request(5, 1); // Request to go to 5th floor at time 1
        Request request2 = new Request(3, 2); // Request to go to 3rd floor at time 2
        Request request3 = new Request(7, 3); // Request to go to 7th floor at time 3

        // Add requests to the system
        Command command1 = new RequestCommand(elevatorSystem, request1);
        Command command2 = new RequestCommand(elevatorSystem, request2);
        Command command3 = new RequestCommand(elevatorSystem, request3);

        command1.execute();
        command2.execute();
        command3.execute();

        // Process requests with FIFO scheduling
        elevatorSystem.processRequests();
    }
}

Explanation of Design Patterns Used

  1. Strategy Pattern:

    • The RequestSchedulingStrategy interface allows the system to dynamically select different request scheduling algorithms (FIFO, SCAN, Priority Weight). By using this pattern, we can easily switch between different strategies at runtime.

  2. Command Pattern:

    • The Command interface, along with the RequestCommand class, encapsulates requests as objects, allowing for better flexibility and decoupling the request creation from request processing.

  3. Factory Pattern:

    • While not explicitly shown here, we could apply the Factory Pattern to instantiate elevators or even scheduling strategies dynamically based on configurations or user inputs.

  4. Observer Pattern:

    • This pattern can be used for notifying passengers when their requested elevator arrives. For example, each Request object could implement an observer that listens for the completion of the elevator’s movement.


Conclusion

The Low-Level Design (LLD) of the Multi-Tier Elevator System efficiently manages multiple elevators, schedules requests using different strategies, and handles command processing through the Command Pattern. By applying Strategy Pattern, we enable the system to easily switch between different request processing algorithms, such as FIFO, SCAN, and Priority Weight. This design is modular and extensible, making it easy to add new features like additional scheduling strategies or complex elevator behaviors.

PreviousRate limiterNextMyGate System

Last updated 2 months ago