Skip to main content

Java - OOPS

· 6 min read

Object-Oriented Programming Basics

ConceptMeaningReal Elevator Use Case
InheritanceOne class derives from another, reusing and extending functionalityDifferent types of elevators: ExpressElevator, FreightElevator extend Elevator
InterfaceA contract defining capabilities without implementationMovable, Openable actions each elevators has
Abstract ClassBase class with partial implementation and some abstract methodsBaseElevator class defines shared logic, but leaves specifics like move() to subclasses
PolymorphismSame method behaves differently based on object typeelevator.handleRequest() works differently for express vs standard elevators
EncapsulationInternal state is hidden, accessed via controlled methodsElevator's currentFloor, direction, and doorStatus are accessed via getters/setters
CompositionBuilding complex objects using other objectsElevator is composed of Door, Motor

Abstract Classes vs Interfaces

Both abstract classes and interfaces help define shared behavior.

  • Abstract classes are like partially prepared classes that you extend to create new classes. It has both fields and methods (which can be implemented or unimplemented - abstract methods)
  • Interfaces are like contracts that define capabilities without implementation. They define what a class must do, but not how it should do it. It doesn't hold state

Elevator OOP Design

Class/ComponentOOP ConceptPurpose
Elevator (abstract)Abstract classProvides shared elevator logic and structure
StandardElevator, ExpressElevatorInheritanceDifferent elevator behaviors extending base functionality
Door, MotorCompositionBuilding blocks that make up a complete elevator
Movable, OpenableInterfacesCapability contracts for elevator components
ElevatorControllerPolymorphismManages different elevator types uniformly

Abstract Class — Elevator

Base class with partial implementation

public abstract class Elevator {

// 🟡 State variables - no behaviors.
// Protected - accessible to child classes
protected int currentFloor;
protected Direction direction;
protected ElevatorState state;

// 🟡 Compositions
// These are classes with behaviors (door.open(), motor.start())
protected final Door door;
protected final Motor motor;
protected final ElevatorPanel panel;

// TreeSet - sorted set. Auto sort on insert
protected TreeSet<Integer> destinationFloors = new TreeSet<>();

public Elevator() {
this.door = new Door();
// ... initialize variables

this.currentFloor = 0;
this.direction = Direction.NONE;
}

// 🟡 Abstract methods - each elevator type must implement these
public abstract void move();
public abstract void handleRequests();

// 🟡 Shared method with concrete implementation
public void pressButton(int floor) {
destinationFloors.add(floor);
}
}

Inheritance — Specific Elevators

Different elevator types share core functionality (abstract class) but behave differently (concrete implementation class).

public class StandardElevator extends Elevator {
@Override
public void move() {
// Standard elevator logic
}

@Override
public void handleRequests() {
// Standard elevators serve all floors
}

// automatically inherits pressButton from Elevator
}

public class ExpressElevator extends Elevator {
@Override
public void move() {
// Can skip floors to reach destinations faster
// Moves the elevator one floor closer to the next destination.
// Updates elevator direction, and state
}

@Override
public void handleRequests() {
// Might prioritize express floors or ignore some requests
// checks whether the elevator has reached a floor someone requested.
// If yes,
// open the door,
// removes that floor from the destination list,
// and sets the elevator's state to IDLE.
}

// automatically inherits pressButton from Elevator
}

Interfaces

Interfaces specify what a class can do, not how it does it.

public interface Movable {
void move();
}

public interface Openable {
void open();
void close();
}

Classes implementing interfaces must provide concrete implementation for the methods

public class Door implements Openable {
public void open() { // do xyz }
public void close() { // do abc }
}

ElevatorController - System Orchestrator

It is responsible for coordinating multiple elevators, distributing request to the right elevator and triggering movement.

public class ElevatorController {

// Using Elevator abstract class - we can use any type of elevator
private final List<Elevator> elevators;

// Queue to hold incoming floor requests
// It's possible all elevators are busy, so we need to hold the request
private final Queue<ElevatorRequest> requestQueue = new LinkedList<>();

public ElevatorController(List<Elevator> elevators) {
this.elevators = elevators;
}

// Called whenever user presses a button (go to floor 5)
public void handleRequest(int requestedFloor) {
requestQueue.add(new ElevatorRequest(requestedFloor));
}

public void step() {
processRequests(); // assign pending requests to elevators
updateElevators(); // let elevators move and process floors
}

private void processRequests() {
// Process all pending requests and try to assign elevator
Iterator<ElevatorRequest> iterator = requestQueue.iterator();

while (iterator.hasNext()) {
ElevatorRequest request = iterator.next();
Elevator bestElevator = selectElevator(request.floor());

if (bestElevator != null) {
bestElevator.pressButton(request.floor());
iterator.remove(); // remove ONLY when it's been assigned
}
}
}

private Elevator selectElevator(int requestedFloor) {
// select the best elevator based on some criteria
}

private void updateElevators() {
for(Elevator elevator : elevators) {
elevator.handleRequests(); // Check if current floor is a destination
elevator.move(); // Move one floor if needed
}
}
}

// Request to move to a specific floor
public record ElevatorRequest(
int targetFloor,
Instant timestamp,
Priority priority
) {}

Elevator System

public class ElevatorSystem {
public static void main() {
List<Elevator> elevators = List.of(
new StandardElevator(),
new ExpressElevator()
);

ElevatorController controller = new ElevatorController(elevators);

// Requests come in at different ticks
controller.handleRequest(5);
controller.handleRequest(1);
controller.handleRequest(10);

// Run simulation
for(int tick = 0; tick < 10; tick++) {
controller.step();
}
}
}

End to End Flow

User presses button to go to floor 5 → controller.handleRequest(5) → adds to request queue.

tick → controller.step()

processRequests() checks if any elevator can serve the request.
selectElevator(5) → selects the best elevator for floor 5
bestElevator.pressButton(5) → adds to elevator's destinationFloors

Elevator handles requests → elevator.handleRequests() → is current floor a destination? if yes, stop and open the door

Elevator moves → elevator.move() → moves the elevator one floor closer to the next destination