// Purpose. Elevator design problem // // Discussion. Each Elevator object "is a" Thread. All other objects are // "hosted" by the "main thread" (this is why Mediator does not need to be a // Thread). The Mediator object models the building's "central allocation // intelligence". It allows all the other objects (Elevators, Floors, and // Doors) to be de-coupled from one another. import java.awt.*; import java.awt.event.*; class Elevator extends Thread { private static Font font = new Font( "Helvetica", Font.BOLD, 20 ); private static Color[] colors = { Color.green, Color.cyan }; private int id; private Mediator med; private Button floorLabel; private Button doorLabel; private Checkbox[] cbs; private int location = 1; private int destination = 1; private int direction = 0; class CBL implements ItemListener { public void itemStateChanged( ItemEvent e ) { moveTo( Integer.parseInt( e.getItem().toString() ) ); } } public Elevator( int num, int floors, Mediator m, Frame w ) { id = num; med = m; Panel all = new Panel(); all.setBackground( colors[id%2] ); all.setForeground( Color.black ); GridLayout gl = new GridLayout(3,1); gl.setVgap( 2 ); all.setLayout( gl ); Panel top = new Panel(); top.setLayout( new GridLayout(1,2) ); floorLabel = new Button( "1" ); floorLabel.setBackground( colors[id%2] ); floorLabel.setEnabled( false ); floorLabel.setFont( font ); top.add( floorLabel ); doorLabel = new Button( "open" ); doorLabel.setBackground( colors[id%2] ); doorLabel.setEnabled( false ); top.add( doorLabel ); all.add( top ); CBL cbl = new CBL(); int divide = floors/2 + floors%2; cbs = new Checkbox[floors]; Panel middle = new Panel(); FlowLayout fl1 = new FlowLayout(); fl1.setHgap( 5 ); middle.setLayout( fl1 ); for (int i=0; i < divide; i++) { cbs[i] = new Checkbox( String.valueOf( i+1 ), false ); cbs[i].addItemListener( cbl ); middle.add( cbs[i] ); } all.add( middle ); Panel bottom = new Panel(); FlowLayout fl2 = new FlowLayout(); fl2.setHgap( 5 ); bottom.setLayout( fl1 ); for (int i=divide; i < floors; i++) { cbs[i] = new Checkbox( String.valueOf( i+1 ), false ); cbs[i].addItemListener( cbl ); bottom.add( cbs[i] ); } all.add( bottom ); w.add( all ); } public int handleRequest( int floor ) { if (direction != 0) return 999; return Math.abs( location - floor ); } public void moveTo( int floor ) { System.out.println( "elevator " + id + " moving to floor " + floor ); if (floor == location) { med.elevatorStopped( location ); cbs[location-1].setState( false ); return; } destination = floor; direction = ((destination > location) ? 1 : -1); doorLabel.setLabel( "closed" ); } public void run() { while (true) { try { sleep(1000); } catch( InterruptedException e ) { } if (direction == 0) continue; // passing my floorLabel widget so that Mediator can change my floor // location label the same time it broadcasts the change to all // affected Door objects med.elevatorChanged( id, floorLabel, direction ); location += direction; if (location == destination) { direction = 0; doorLabel.setLabel( "open" ); med.elevatorStopped( destination ); cbs[destination-1].setState( false ); } } } } class Floor { private int id; private Mediator med; private Button up; private Button dn; class BL implements ActionListener { public void actionPerformed( ActionEvent e ) { ((Component)e.getSource()).setBackground( Color.yellow ); ((Component)e.getSource()).setForeground( Color.red ); med.requestElevator( id, (e.getActionCommand().equals("up") ? true : false ) ); } } public Floor( int i, Mediator m, Frame w ) { id = i; med = m; Panel p = new Panel(); p.setBackground( Color.red ); p.setForeground( Color.yellow ); p.setLayout( new GridLayout(1,2) ); BL bl = new BL(); up = new Button( "up" ); up.setBackground( Color.red ); up.addActionListener( bl ); p.add( up ); dn = new Button( "down" ); dn.setBackground( Color.red ); dn.addActionListener( bl ); p.add( dn ); w.add( p ); } public void resetButtons() { up.setBackground( Color.red ); up.setForeground( Color.yellow ); dn.setBackground( Color.red ); dn.setForeground( Color.yellow ); } } class Door { private Button floor; private static Font font = new Font( "Helvetica", Font.BOLD, 40 ) ; public Door( Frame w ) { floor = new Button( "1" ); floor.setBackground( Color.white ); floor.setEnabled( false ); floor.setFont( font ); w.add( floor ); } public void setLabel( String str ) { floor.setLabel( str ); } } class Mediator { private Elevator[] elevs; private Floor[] flors; private Door[][] doors; private Frame win; public Mediator( int fls, int els ) { win = new FrameClose( "Elevators - Floors - Doors" ); win.setLayout( new GridLayout(fls+1, els+1) ); Label lab = new Label(); lab.setBackground( Color.blue ); win.add( lab ); flors = new Floor[fls]; elevs = new Elevator[els] ; doors = new Door[fls][els]; for (int i=0; i < elevs.length; i++) { elevs[i] = new Elevator( i+1, fls, this, win ); elevs[i].start(); } for (int i=0; i < fls; i++) { flors[i] = new Floor( fls-i, this, win ); for (int j=0; j < els; j++) doors[i][j] = new Door( win ); } win.pack(); win.setVisible( true ); } public void requestElevator( int floor, boolean up ) { System.out.println("floor " +floor +" wants to go " +(up ? "up":"down")); int min = 999, ans, choose = 0; while (min == 999) for (int i=0; i < elevs.length; i++) { ans = elevs[i].handleRequest( floor ); if (ans < min) { min = ans; choose = i; } } elevs[choose].moveTo( floor ); } public void elevatorStopped( int floor ) { flors[flors.length-floor].resetButtons(); } public void elevatorChanged( int id, Button b, int amt ) { String str = String.valueOf( Integer.parseInt(b.getLabel()) + amt ); b.setLabel( str ); for (int i=0; i < flors.length; i++) doors[i][id-1].setLabel( str ); } public static void main( String[] args ) { if (args.length != 2) { System.out.println( "usage: dorun Mediator #floors #elevators" ); System.exit( 0 ); } new Mediator( Integer.parseInt( args[0] ), Integer.parseInt( args[1] ) ); } } // Elevator System Requirements --- // // The system will support a variable number of elevators and floors. // // Each elevator presents a row of "buttons" that allow users to select the // desired floor. When a selection is made, the door closes, and the // elevator moves to that floor. As the elevator travels, an indication of // the "current floor" is displayed. When the floor is reached, the door // opens, and the floor selection mechanism is cleared. // // Each floor will allow users to make "up" and "down" requests. When a // selection is made: the "closest available elevator" is sent to the floor, // the door opens, and the floor's up/down request mechanism is reset. // // As elevators travel, each floor will maintain an indication of the // location of each elevator.