State Pattern (padrão de desenho)/Exercício 01: Semáforo

From Wiki**3

< State Pattern (padrão de desenho)
Revision as of 01:22, 11 November 2009 by Root (talk | contribs)

Problema

Um semáforo tem um temporizador que emite impulsos a intervalos regulares. Estes impulsos chegam ao controlador do semáforo sob a forma de chamadas ao método tick. O semáforo tem ainda vários botões para controlo de situações especiais. Assim, o botão de pânico que faz com que o controlador mude o semáforo para a cor vermelha, qualquer que seja a anterior (método panic). O botão off faz com que o semáforo mude imediatamente para amarelo intermitente (método off). Esta situação é interrompida através dos botões on (volta ao comportamento normal; método on) ou de pânico. O botão on é a única forma de fazer semáforo sair da situação de pânico e recuperar o funcionamento normal. Além do código de controlo, o semáforo tem ainda um método (status) que permite saber a cor actual do semáforo. O semáforo começa intermitente e quando recupera das situações de intermitência e de pânico fica vermelho.

Implemente o semáforo e a sua máquina de estados.

Solução

Classe TrafficLight (externa) e TrafficLight.State (interna)

<java5> /**

* @author David Martins de Matos
*/

public class TrafficLight {

/** * The traffic light's state. */ private State _state;

/** * Abstract state class. * * This class is internal so that it has access to the traffic light's * internal state. Actual states are subclasses which must use this class' * protected interface. */ public abstract class State {

/** * Tick behavior. */ public abstract void tick();

/** * Panic behavior. */ public abstract void panic();

/** * "On" behavior. */ public void on() { // ignore }

/** * "Off" behavior. */ public abstract void off();

/** * @return traffic light status. */ public abstract String status();

/** * Define the traffic light's new state. * * @param newState * the new state. */ protected void setState(State newState) { _state = newState; }

/** * This method is needed so that new states can be created. * * @return the traffic light. */ protected TrafficLight getLight() { return TrafficLight.this; } }

/** * Initialize traffic light. Starts blinking. */ public TrafficLight() { _state = new Blinking(this); }

/** * Process on button press. */ @SuppressWarnings("nls") public void on() { System.out.print("[" + status() + "]"); _state.on(); System.out.println(" --(on)-> [" + status() + "]"); }

/** * Process off button press. */ @SuppressWarnings("nls") public void off() { System.out.print("[" + status() + "]"); _state.off(); System.out.println(" --(off)-> [" + status() + "]"); }

/** * Process panic button press. */ @SuppressWarnings("nls") public void panic() { System.out.print("[" + status() + "] --(panic)-> "); _state.panic(); System.out.println("[" + status() + "]"); }

/** * Process tick: switch light color. */ @SuppressWarnings("nls") public void tick() { System.out.print("[" + status() + "] --(tick)-> "); _state.tick(); System.out.println("[" + status() + "]"); }

/** * @return traffic light status. */ public String status() { return _state.status(); }

} </java5>

Classe Ticking

Abstract class for representing normal behavior. See also the next section (color classes).

<java5> /**

* Ticking states are those corresponding to the usual three colors.
*/

public abstract class Ticking extends TrafficLight.State {

/** * @param light */ Ticking(TrafficLight light) { light.super(); }

/** * All colors switch to blinking when "off" is pressed. * * @see TrafficLight.State#off() */ @Override public void off() { setState(new Blinking(getLight())); }

/** * All colors switch to panic when the button is pressed. * * @see TrafficLight.State#panic() */ @Override public void panic() { setState(new Panic(getLight())); }

} </java5>

Classes Red, Yellow, Green

Red light.

<java5> /**

* The red light.
*/

public class Red extends Ticking {

/** * @param light */ Red(TrafficLight light) { super(light); }

/** * @see TrafficLight.State#tick() */ @Override public void tick() { setState(new Green(getLight())); }

/** * @see TrafficLight.State#status() */ @Override @SuppressWarnings("nls") public String status() { return "Red"; } } </java5>

Yellow light.

<java5> /**

* The yellow light.
*/

public class Yellow extends Ticking {

/** * @param light */ Yellow(TrafficLight light) { super(light); }

/** * @see TrafficLight.State#tick() */ @Override public void tick() { setState(new Red(getLight())); }

/** * @see TrafficLight.State#status() */ @Override @SuppressWarnings("nls") public String status() { return "Yellow"; } } </java5>

Green light.

<java5> /**

* The green light.
*/

public class Green extends Ticking {

/** * @param light */ Green(TrafficLight light) { super(light); }

/** * @see TrafficLight.State#tick() */ @Override public void tick() { setState(new Yellow(getLight())); }

/** * @see TrafficLight.State#status() */ @Override @SuppressWarnings("nls") public String status() { return "Green"; } } </java5>

Classe Panic

<java5> /**

* Panic state.
*/

public class Panic extends TrafficLight.State {

/** * @param light */ Panic(TrafficLight light) { light.super(); }

/** * Return to "ticking" (normal) mode. * * @see TrafficLight.State#on() */ @Override public void on() { setState(new Red(getLight())); }

/** * @see TrafficLight.State#off() */ @Override public void off() { // do nothing: already off }

/** * @see TrafficLight.State#panic() */ @Override public void panic() { // do nothing: already in panic mode }

/** * @see TrafficLight.State#tick() */ @Override public void tick() { // do nothing: ignore ticks in panic mode }

/** * @see TrafficLight.State#status() */ @Override @SuppressWarnings("nls") public String status() { return "Panic"; } } </java5>

Classe Blinking

<java5> /**

* Blinking light (off).
*/

public class Blinking extends TrafficLight.State {

/** * @param light */ Blinking(TrafficLight light) { light.super(); }

/** * Return to "ticking" (normal) mode. * * @see TrafficLight.State#on() */ @Override public void on() { setState(new Red(getLight())); }

/** * @see TrafficLight.State#off() */ @Override public void off() { // do nothing: already off }

/** * @see TrafficLight.State#panic() */ @Override public void panic() { setState(new Panic(getLight())); }

/** * @see TrafficLight.State#tick() */ @Override public void tick() { // do nothing: ignore ticks }

/** * @see TrafficLight.State#status() */ @Override @SuppressWarnings("nls") public String status() { return "Blinking"; } } </java5>

Classe App

Simple application to illustrate the traffic light's state changes.

<java5> /**

* Simple application to illustrate changes of light states.
*/

public class App { /** * @param args */ public static void main(String[] args) { TrafficLight light = new TrafficLight(); System.out.println("Light status: " + light.status()); light.off(); light.panic(); light.on(); light.tick(); light.tick(); light.tick(); light.tick(); light.panic(); light.off(); light.on(); light.off(); } } </java5>

Compiling and Running

Compiling

Running

java App

Result:

Light status: Blinking
[Blinking] --(off)-> [Blinking]
[Blinking] --(panic)-> [Panic]
[Panic] --(on)-> [Red]
[Red] --(tick)-> [Green]
[Green] --(tick)-> [Yellow]
[Yellow] --(tick)-> [Red]
[Red] --(tick)-> [Green]
[Green] --(panic)-> [Panic]
[Panic] --(off)-> [Panic]
[Panic] --(on)-> [Red]
[Red] --(off)-> [Blinking]