I did not find a fair complete solution to this OOD, so I write my own. I was struggled with the elevator scheduling algorithms, most of the solutions online directly goes with the implementation but without explanning anthing, any context. Of course, you might not need to touch on the implementation during the interview if interviewee does not requires you or you do not have much time. But at least you need to know some basics. Barely I saw someone mention or give a summary of the algorithms. Without summary, it cannot help and broaden developer's view(that's why I like the book DDIA, which gives detailed summary, and clarify ppl's doubts that ppl have occuring in their minds in sometimes and don't know or forget to ask..)
Clarify
Key Questions
List<Elevator> elevators = new ArrayList<>();Other Questions - The questions I encoutered while went through online, but I think probably it is not necessary and minor
Elevator Scheduling System
Also before going into OOD details, I want to spend some time about the "Elevator Scheduling System". I did went through many articles about this OOD design, but mostly they just directly go to the scheduling implementation, implicitly take some algorithms.
Elevator Scheduling System can be complicated, before I prepare this OOD, I thought all the elevators in the world has a very simple, common, best scheduling algorithm. However I found out that this is not the truth, there are many algorithms for it(Some ppl even mentioned it is a trade secret...). Based on https://cdmana.com/2021/02/20210202111024127g.html(equivalent Chinese version 中文版: https://cdmana.com/2021/02/20210202090419089B.htm), algorithms can be catogorized as below:
Traditional Algorithm
Some resources in Chinese
Realtime Scheduling Algorithms
Anyway, the conclusion is that there are many algorithms, no one is perfect, it is NP-Hard problem for 1 elevator without capacity constraint based on http://www.columbia.edu/~cs2035/courses/ieor4405.S13/p14.pdf(not sure for multiple and with capacity constraint, if it is still NP-Hard or not?) I have seen some dicussion saying that oh, this solution is not good blablabla because someone may wait for a long time blablabla...But I think it is fine, it is open-ended, should not be the key of OOD design. We can take the simple strategy, the elevator will first process UP requests where request floor is greater than current floor and then process Down requests where request floor is lower than current floor.
Core
Controller
The best part I like is the Controller, which I only see in one of the solutions online. Most of the solutions either did not mention this part(they stop when they put external or internal requests to the queue or array). But it makes more sense to me to indicate how we really move the elevator, all the commands will be extecuted by the machinical device, right? So "Controller" will be used to represent the machine devide,
Skip User Cases Stage here
Simply say
Basic
I did not implement it, because
Optional
Code
I will not add getters and setters methods here for simplicity, and just image I have lombok configured here.
Note that we update upStops and downStops based on if elevetor should go up or down, not based on whether a person need to go up or down.(The implementation is definitely not 100% good or even correct, I might need to think about it more later..)
public enum Direction{
UP,
DOWN,
STOPPED, // some solution does consider this and timeout for waiting passengers to come inside
IDLE
}
public class Request{
Type type;
int floor;
public Request(int floor){
this.floor = floor;
}
}
public class InternalRequest extends Request{
public InternalRequest(int floor){
super(floor);
// type = Type.INSIDE;
}
}
public class ExternalRequest extends Request{
Direction direction;
public ExternalRequest(int floor, Direction direction){
super(floor);
// type = Type.OUTSIDE;
}
}
public class Button{
int floor;
public Button(int floor){
this.floor = floor;
}
}
public class Elevator{
public Status status;
public int currentFloor;
public boolean[] upStops;
public boolean[] downStops;
public int upStopsCount;
public int downStopsCount;
public Button[] buttons; // or List<Button> buttons
public Controller controller;
pubic Elevator(){
direction = Direction.IDLE;
currentFloor = 1;
upStops = new boolean[n];
downStops = new boolean[n];
controller = new Controller();
// deprecated
// upRequests = new PriorityQueue<>((a, b) -> Integer.compare(a.floor, b.floor));
// downRequests = new PriorityQueue<>((a, b) -> Integer.compare(b.floor, a.floor));
}
public void processUpRequest(){
if(this.direction == Direction.IDLE) return;
for(int i = this.currentFloor + 1; i < upStops.length; i++){
if(upStops[i]){
this.controller.goUp(i);
openGate();
closeGate();
}
}
// all the left requests will be down requests
// if none, the elevator is in idle state
if(downRequestsCount == 0){
this.direction = Direction.IDLE;
}else{
this.direction = Direction.DOWN;
}
}
public void processDownRequest(){
if(this.direction == Direction.IDLE) return;
for(int i = this.currentFloor - 1; i >= 0; i--){
if(downStops[i]){
this.controller.goDown(i);
openGate();
closeGate();
}
}
if(upRequestsCount == 0){
this.direction = Direction.IDLE;
}else{
this.direction = Direction.UP;
}
}
public void processButton(Button button){
addRequest(new InternalRequest(button.floor));
}
// we update elevator's status when add request, or we can modify to add button design here, replace parameter from "request" to "button"
public void addRequest(Request request){
if(this.direction = Direction.UP){
if(request.floor > this.currentFloor) {
upStops[request.floor] = true;
upStopsCount++;
}
else{
downStops[request.floor] = true;
downStopsCount++;
}
}else if(this.direction = Direction.DOWN){
if(request.floor < this.currentFloor) {
downStops[request.floor] = true;
downStopsCount++;
else {
upStops[request.floor] = true;
upStopsCount++;
}
} else{
// if it is IDLE, then direction depends on current level and target level
if(request.floor > this.currentFloor){
upStops[request.floor] = true;
upStopsCount++;
this.direction = Direction.UP;
}else{
downStops[request.floor] = true;
downStopsCount++;
this.direction = Direction.DOWN;
}
}
}
public void openGate(Direction direction){
this.controller.openGate();
this.currentFloor = i;
if(direction = Direction.UP){
this.currentFloor = i;
upStops[i] = false;
upRequestsCount--;
}else{
downStops[i] = false;
downRequestsCount--;
}
// wait for passengers to get in or out, e.g.
this.controller.sleep(10); // sleep for 10 seconds
}
public void closeGate(){
this.controller.closeGate();
}
public void start(){
while(true){
processUpRequests();
processDownRequest();
}
}
}