Difference between revisions of "State Pattern (padrão de desenho)/Exercício 01: Semáforo"

From Wiki**3

< State Pattern (padrão de desenho)
Line 1: Line 1:
 +
{{TOCright}}
 
= Problema =
 
= 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.
+
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.
 
Implemente o semáforo e a sua máquina de estados.
Line 7: Line 8:
 
= Solução =
 
= 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]
  
 
[[category:PO]]
 
[[category:PO]]
 
[[category:Ensino]]
 
[[category:Ensino]]

Revision as of 01:22, 11 November 2009

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]