Design patterns to be used in elevator design
Designing an elevator system involves solving complex problems like request handling, optimization, concurrency, and scalability. Various design patterns can be employed to address these challenges.
1. Singleton Pattern
Use Case: To ensure a single instance of the
ElevatorSystem
class manages all the elevators and requests in the building.Why?: The system should be globally accessible and prevent multiple instances to avoid conflicts in request handling.
Example:
public class ElevatorSystem {
private static ElevatorSystem instance;
private ElevatorSystem() { }
public static synchronized ElevatorSystem getInstance() {
if (instance == null) {
instance = new ElevatorSystem();
}
return instance;
}
}
2. Observer Pattern
Use Case: To notify elevators when a new floor request is made.
Why?: Floors can act as subjects, and elevators can be observers that are notified of requests.
Example:
Floor
acts as the subject.Elevator
acts as an observer.
public interface Observer {
void update(int floorNumber, Direction direction);
}
public class Floor {
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void notifyObservers(int floorNumber, Direction direction) {
for (Observer observer : observers) {
observer.update(floorNumber, direction);
}
}
}
3. Strategy Pattern
Use Case: For scheduling elevators (e.g.,
Nearest Elevator First
,Least Loaded Elevator
).Why?: To decouple the scheduling algorithm from the system, making it easier to switch or add new strategies.
public interface SchedulingStrategy { void schedule(List<Elevator> elevators, FloorRequest request); } public class NearestElevatorStrategy implements SchedulingStrategy { public void schedule(List<Elevator> elevators, FloorRequest request) { // Implement nearest elevator logic } }
4. Command Pattern
Use Case: To encapsulate requests as objects and queue them for execution.
Why?: Decouples the requester (floor) from the executor (elevator).
public interface Command { void execute(); } public class FloorRequestCommand implements Command { private Elevator elevator; private int floor; public FloorRequestCommand(Elevator elevator, int floor) { this.elevator = elevator; this.floor = floor; } public void execute() { elevator.addFloorToQueue(floor); } }
5. Factory Pattern
Use Case: For creating different types of elevators (
Standard
,Freight
, etc.).Why?: To encapsulate the object creation logic.
public class ElevatorFactory { public static Elevator createElevator(String type) { switch (type) { case "Standard": return new StandardElevator(); case "Freight": return new FreightElevator(); default: throw new IllegalArgumentException("Unknown elevator type"); } } }
6. State Pattern
Use Case: To manage the state of an elevator (
Idle
,MovingUp
,MovingDown
,Stopped
).Why?: To simplify state transitions and ensure elevators behave correctly.
public interface ElevatorState {
void handleRequest(Elevator elevator, FloorRequest request);
}
public class IdleState implements ElevatorState {
public void handleRequest(Elevator elevator, FloorRequest request) {
// Transition to Moving state and process request
}
}
7. Builder Pattern
Use Case: For constructing complex elevator objects with multiple configurations.
Why?: Makes the construction process flexible and readable.
public class Elevator { private int capacity; private int currentFloor; private Elevator(Builder builder) { this.capacity = builder.capacity; this.currentFloor = builder.currentFloor; } public static class Builder { private int capacity; private int currentFloor; public Builder setCapacity(int capacity) { this.capacity = capacity; return this; } public Builder setCurrentFloor(int currentFloor) { this.currentFloor = currentFloor; return this; } public Elevator build() { return new Elevator(this); } } }
8. Thread Pool Pattern
Use Case: For managing concurrent user requests efficiently.
Why?: Prevents resource exhaustion and improves performance.
ExecutorService executor = Executors.newFixedThreadPool(5); executor.submit(() -> elevatorSystem.handleRequest(new FloorRequest(3, Direction.UP)));
9. Mediator Pattern
Use Case: To manage communication between floors and elevators.
Why?: Simplifies interactions between components.
public class ElevatorMediator {
private List<Elevator> elevators;
public ElevatorMediator(List<Elevator> elevators) {
this.elevators = elevators;
}
public void handleRequest(FloorRequest request) {
// Route the request to the appropriate elevator
}
}