Elevator design challenge [closed]

I need to strengthen my OOP skills and hence I thought to implement an Elevator simulator. At first what seem to be a simple design has grown into a complete mess and has left me confused about my knowledge of OOP. Below is the implementation classes.

/**
 * 1. User enter the elevator.
 * 2. Elevator checks the maximum capacity.
 * 3. User enters the floor number.
 * 4. Elevator registers the request.
 * 5. Elevator checks the next request and stops at that floor.
 * 6. When elevator is in the halt state the door opens.
 */

enum State {
  UP,
  DOWN,
  HALT
}

enum Command {
  ALARM,
  OPEN,
  CLOSE,
  GROUND,
  FIRST,
  SECOND
}

enum Floor {
  GROUND,
  FIRST,
  SECOND
}

interface Engine {
  public void moveTO(Floor floor);
}

interface Alarm {
  public void ring();
}

interface Sensor {
  public int getWeight();
  public int shouldCloseDoor();
}

final class Panel {
  private final Command[] commands;
  private final Elevator elevator;

  Panel(Command[] commands, Elevator elevator) {
    this.commands = commands;
    this.elevator = elevator;
  }

  public void onKeyPress(Command command) {
    if (command == Command.OPEN) {
      elevator.stop();
    } else if (command == Command.CLOSE) {
      elevator.move();
    } else if (command == Command.GROUND) {
      elevator.gotoFloor(Floor.GROUND);
    } else if (command == Command.FIRST) {
      elevator.gotoFloor(Floor.FIRST);
    } else if (command == Command.SECOND) {
      elevator.gotoFloor(Floor.SECOND);
    }
  }
}

final class Door {
  private boolean final open;

  Door() {
    open = false;
  }

  public void open() {
    if (open) return;
    try {
      Thread.sleep(1000); //1000 milliseconds is one second.
      open = true;
    } catch(InterruptedException ex) {
      Thread.currentThread().interrupt();
      close();
    }
  }

  public void close() {
    open = false;
  }
}

// Handle elevator logic. Here the assumption is that the elevator has to come
// back to ground floor if there are not any pending requests. There are two
// queues which handles the floor requests, the elevator keeps moving in the
// direction of the queue until that queue is not empty. If the elevator reaches 
// the top floor and there is no pending request then the elevator comes down to
// ground floor and comes to the halting state.
class Elevator {
  public static final int MAX_WEIGHT_ALLOWED = 1600;

  private final State state;
  private final Door door;
  private final Floor floor;
  private final Queue<Floor> queueUp;
  private final Queue<Floor> queueDOwn;
  private final Engine engine;
  private final Sensor sensor;
  private final Alaram alarm;

  Elevator(Door door, Engine engine, Sensor sensor, Alaram alarm) {
    this.door  = door;
    this.queueUp   = new Queue<Floor>();
    this.queueDown = new Queue<Floor>();
    this.state  = State.HALT
    this.floor  = Floor.ZERO;
    this.engine = engine;
    this.sensor = sensor;
    this.alarm  = alarm;
  }

  public void stop() {
    door.open();
    state = State.HALT;
    engine.stop();
    System.out.println("Elevator is in stopped state.");
  }

  private void moveUp() {
    while(!queueUp.isEmpty()) {
      floor = queueUp.dequeue();
      engine.moveTO(floor);
    }
    // All up requests are served.
    state = state.DOWN;
    gotoFloor(Floor.ZERO);
  }

  private void moveDown() {
    while(!queueDown.isEmpty()) {
      floor = queueUp.dequeue();
      engine.moveTO(floor);
    }
    // All down requests served.
    state = State.UP;
  }

  public void move() {
    if (sensor.getWeight() > Elevator.MAX_WEIGHT_ALLOWED) {
      alarm.ring();
      stop();
      return;
    }
    if (!sensor.shouldCloseDoor()) {
      alarm.ring();
      stop();
      return;
    }
    door.close();
    if (state == State.UP) {
      moveUp();
    } else if (state == State.DOWN) {
      moveDOwn();
    }
    // Both the queues are empty
    state = State.HALT;
    System.out.println("Elevator is moving to floor: " + );
  }

  public void gotoFloor(Floor nextFloor) {
    if (floor < nextFloor)
      queueDown.enqueue(floor);
    else
      queueUp.enqueue(floor);
    move();
  }
}


public class ElevatorSimulator {
  public static void main(String[] args) {
    Elevator elevator = new Elevator(
      new Door(), new Engine(), new Sensor(), new Alarm());
    Panel panel = new Panel(Command.values(), elevator);

    panel.onKeyPress(Command.OPEN);
    panel.onKeyPress(Command.CLOSE);
    panel.onKeyPress(Command.FIRST);
  }
}

I have heard that thinking and jotting down the requirement helps a lot and I tried it too but I think I completely failed in that scenario too.

Question

  1. Good strategy to settle down on a set of requirements.
  2. Avoid cognitive overload and concentrate on the high level design rather than switching to distracting details.
  3. List and manage dependencies.
  4. Overall how to approach a good design 🙂

12

You are trying to create a layered design.

In the top layer are inputs & outputs that interact with the elevator riders: the inputs are the button panel in the elevator and the displays of what floor it’s on along with the up/down indicator and button pressed indicators. Further there are up/down call buttons on each floor (and a duplication of the displays). This part is pretty straightforward, you have panel of buttons in the elevator and a variable (per-floor) set of up/down call buttons, and a display (that gets duplicated). (Unless, of course, you get experimental or fancy you could try to improve elevator function by having different buttons than are standard…)

In the middle is an abstraction that hides the implementation details, namely the algorithm that chooses fairness and floors and balances the per-floor requests against the other per-floor requests and on-elevator requests.

At the lowest layer, there are the sensors to read and actuators to be controlled. Here’s where it seems to me that you need more rigor. The design elements seem uneven to me. The Door is represented by a class whereas the Engine by an interface. There is a single sensor interface. I’d expect to see multiple sensors of specific types. The sensor interface has an iffy should-door-close method. You also need to call out actuators clearly.

You should not design the algorithm or codify classes until you fully understand this lowest layer. You need to give some serious thought or research to these capabilities, and how intelligent they are. For example, will the motor allow activation to move the elevator regardless of whether the door is open? If so, then your elevator algorithm will have to prevent moving while the door is open. If not, then your elevator algorithm is sharing some of the intelligence and safety handling with another semi-intelligent controller, which means you should not try to duplicate, replicate, (or necessarily cache) those capabilities within your elevator algorithm.

There is also a configuration of the elevator: the biggest item of which is simply the count of the actual number of floors, as for each floor we can expect an on-floor panel for up/down request buttons, not to mention that the in-elevator panel buttons reflect the floor count.

Fundamentally, you need to decide what you are modeling by your code and why. There’s a saying that you don’t model something without a purpose: the model is not just to look at internally and admire, but to do something very specific externally. The answer to “why we model something” is because you need to exhibit a behavior of responding to inputs, and specifically, that behavior manifests in the triggering(s) of the appropriate actuator(s) at the right time(s). Otherwise, don’t model what you don’t need. Don’t duplicate modeling of something that is already modeled by something else. For example, if the door knows whether it is open or not, then use that interface rather than the duplicate the door open/closed state in your algorithm.

So, you start at the top layer and specify the interfaces that provide for the buttons and displays. Then identify the sensors and their interfaces, and the actuators that need controlling and their interfaces. Next figure out where the intelligence is in the system (what’s missing and where it needs to be), which depends largely on how smart the sensor/actuator devices are. Then you can design a smart & safe elevator handler that sits in between the top (user) layer and the lowest (sensor/actuator) layer.

Essentially, you need to design from the outside in (top & bottom first, then middle); otherwise, you’ll have no idea of what you are modeling (in the middle) and why.

2

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *