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

From Wiki**3

< State Pattern (padrão de desenho)
(Solução)
Line 11: Line 11:
  
 
Notar os aspectos de herança de classes internas e a forma especial de chamada ao contrutor da superclasse interna. Notar ainda como uma classe interna (State) pode aceder ao objecto actual da classe externa (TrafficLight.this).
 
Notar os aspectos de herança de classes internas e a forma especial de chamada ao contrutor da superclasse interna. Notar ainda como uma classe interna (State) pode aceder ao objecto actual da classe externa (TrafficLight.this).
 +
 +
Notar ainda que não é estritamente necessário que a superclasse das classes dos estados seja interna à classe detentora do estado ('''TrafficLight'''). Optou-se por definir a classe como interna para demonstrar como a classe interna pode aceder a estado privado da folha.
  
 
== Classe TrafficLight (externa) e TrafficLight.State (interna) ==
 
== Classe TrafficLight (externa) e TrafficLight.State (interna) ==
  
 
<java5>
 
<java5>
/**
 
* @author David Martins de Matos
 
*/
 
 
public class TrafficLight {
 
public class TrafficLight {
  
/**
+
/** The traffic light's state. */
* The traffic light's state.
 
*/
 
 
private State _state;
 
private State _state;
  
Line 34: Line 31:
 
public abstract class State {
 
public abstract class State {
  
/**
+
/** Tick behavior. */
* Tick behavior.
 
*/
 
 
public abstract void tick();
 
public abstract void tick();
  
/**
+
/** Panic behavior. */
* Panic behavior.
 
*/
 
 
public abstract void panic();
 
public abstract void panic();
  
/**
+
/** "On" behavior. */
* "On" behavior.
+
public void on() { /* ignore by default */ }
*/
 
public void on() {
 
// ignore
 
}
 
  
/**
+
/** "Off" behavior. */
* "Off" behavior.
 
*/
 
 
public abstract void off();
 
public abstract void off();
  
/**
+
/** @return traffic light status. */
* @return traffic light status.
 
*/
 
 
public abstract String status();
 
public abstract String status();
  
Line 91: Line 76:
 
* Process on button press.
 
* Process on button press.
 
*/
 
*/
@SuppressWarnings("nls")
 
 
public void on() {
 
public void on() {
 
System.out.print("[" + status() + "]");
 
System.out.print("[" + status() + "]");
Line 101: Line 85:
 
* Process off button press.
 
* Process off button press.
 
*/
 
*/
@SuppressWarnings("nls")
 
 
public void off() {
 
public void off() {
 
System.out.print("[" + status() + "]");
 
System.out.print("[" + status() + "]");
Line 111: Line 94:
 
* Process panic button press.
 
* Process panic button press.
 
*/
 
*/
@SuppressWarnings("nls")
 
 
public void panic() {
 
public void panic() {
 
System.out.print("[" + status() + "] --(panic)-> ");
 
System.out.print("[" + status() + "] --(panic)-> ");
Line 121: Line 103:
 
* Process tick: switch light color.
 
* Process tick: switch light color.
 
*/
 
*/
@SuppressWarnings("nls")
 
 
public void tick() {
 
public void tick() {
 
System.out.print("[" + status() + "] --(tick)-> ");
 
System.out.print("[" + status() + "] --(tick)-> ");
Line 134: Line 115:
 
return _state.status();
 
return _state.status();
 
}
 
}
 
 
}
 
}
 
</java5>
 
</java5>
Line 141: Line 121:
  
 
Abstract class for representing normal behavior. See also the next section (color classes).
 
Abstract class for representing normal behavior. See also the next section (color classes).
 +
 +
Ticking states are those corresponding to the usual three colors.
  
 
<java5>
 
<java5>
/**
 
* Ticking states are those corresponding to the usual three colors.
 
*/
 
 
public abstract class Ticking extends TrafficLight.State {
 
public abstract class Ticking extends TrafficLight.State {
  
Line 174: Line 153:
 
setState(new Panic(getLight()));
 
setState(new Panic(getLight()));
 
}
 
}
 
 
}
 
}
 
</java5>
 
</java5>
Line 183: Line 161:
  
 
<java5>
 
<java5>
/**
 
* The red light.
 
*/
 
 
public class Red extends Ticking {
 
public class Red extends Ticking {
  
Line 207: Line 182:
 
*/
 
*/
 
@Override
 
@Override
@SuppressWarnings("nls")
 
 
public String status() {
 
public String status() {
 
return "Red";
 
return "Red";
Line 217: Line 191:
  
 
<java5>
 
<java5>
/**
 
* The yellow light.
 
*/
 
 
public class Yellow extends Ticking {
 
public class Yellow extends Ticking {
  
Line 241: Line 212:
 
*/
 
*/
 
@Override
 
@Override
@SuppressWarnings("nls")
 
 
public String status() {
 
public String status() {
 
return "Yellow";
 
return "Yellow";
Line 251: Line 221:
  
 
<java5>
 
<java5>
/**
 
* The green light.
 
*/
 
 
public class Green extends Ticking {
 
public class Green extends Ticking {
  
Line 275: Line 242:
 
*/
 
*/
 
@Override
 
@Override
@SuppressWarnings("nls")
 
 
public String status() {
 
public String status() {
 
return "Green";
 
return "Green";
Line 283: Line 249:
  
 
== Classe Panic ==
 
== Classe Panic ==
 +
 +
Panic state.
  
 
<java5>
 
<java5>
/**
 
* Panic state.
 
*/
 
 
public class Panic extends TrafficLight.State {
 
public class Panic extends TrafficLight.State {
  
Line 335: Line 300:
 
*/
 
*/
 
@Override
 
@Override
@SuppressWarnings("nls")
 
 
public String status() {
 
public String status() {
 
return "Panic";
 
return "Panic";
Line 343: Line 307:
  
 
== Classe Blinking ==
 
== Classe Blinking ==
 +
 +
Blinking light (off).
  
 
<java5>
 
<java5>
/**
 
* Blinking light (off).
 
*/
 
 
public class Blinking extends TrafficLight.State {
 
public class Blinking extends TrafficLight.State {
  
Line 395: Line 358:
 
*/
 
*/
 
@Override
 
@Override
@SuppressWarnings("nls")
 
 
public String status() {
 
public String status() {
 
return "Blinking";
 
return "Blinking";
Line 407: Line 369:
  
 
<java5>
 
<java5>
/**
 
* Simple application to illustrate changes of light states.
 
*/
 
 
public class App {
 
public class App {
 
/**
 
/**

Revision as of 12:13, 14 November 2013

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

A solução apresentada considera que a superclasse dos estados é interna (tem, desse modo, acesso ao estado interno do semáforo e não é necessário exportar nenhuma interface para gestão de estados.

Notar os aspectos de herança de classes internas e a forma especial de chamada ao contrutor da superclasse interna. Notar ainda como uma classe interna (State) pode aceder ao objecto actual da classe externa (TrafficLight.this).

Notar ainda que não é estritamente necessário que a superclasse das classes dos estados seja interna à classe detentora do estado (TrafficLight). Optou-se por definir a classe como interna para demonstrar como a classe interna pode aceder a estado privado da folha.

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

<java5> 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 by default */ }

/** "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. */ public void on() { System.out.print("[" + status() + "]"); _state.on(); System.out.println(" --(on)-> [" + status() + "]"); }

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

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

/** * Process tick: switch light color. */ 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).

Ticking states are those corresponding to the usual three colors.

<java5> 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> 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 public String status() { return "Red"; } } </java5>

Yellow light.

<java5> 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 public String status() { return "Yellow"; } } </java5>

Green light.

<java5> 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 public String status() { return "Green"; } } </java5>

Classe Panic

Panic state.

<java5> 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 public String status() { return "Panic"; } } </java5>

Classe Blinking

Blinking light (off).

<java5> 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 public String status() { return "Blinking"; } } </java5>

Classe App

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

<java5> 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]