State Pattern (padrão de desenho)/Exercício 02: Máquina de Lavar

From Wiki**3

< State Pattern (padrão de desenho)

Máquina de Lavar

Uma máquina de lavar tem um mecanismo que gere a abertura da porta, por forma a evitar acidentes com crianças. A máquina tem os seguintes botões e sensores: botão “ligar/desligar” (chama o método power); botão “abrir porta” (método open); sensor “porta fechada” (método closed). A máquina tem ainda um temporizador que emite um impulsos a cada segundo (por cada impulso é chamado o método tick). A máquina apenas pode ser ligada (botão “ligar/desligar”) se a porta estiver fechada: a máquina arranca e apenas pára quando o número de impulsos for igual a 5400 ou quando o botão “ligar/desligar” for premido, interrompendo a lavagem. Quer num caso, quer noutro, a máquina não permite abertura imediata da porta: é necessário esperar 120 impulsos para que a máquina reaja ao botão “abrir porta” (durante o tempo de espera, o botão “ligar/desligar” é ignorado). Caso a porta tenha sido fechada, mas não se tenha iniciado a lavagem, a abertura da porta é imediata. A máquina está inicialmente desligada e a porta está aberta. Represente a abertura da porta através da impressão de uma cadeia de caracteres.

Implemente todas as classes da máquina de lavar com controlador de segurança.

Solução

Usa-se o padrão State para controlo da porta da máquina.

A máquina é simples: contém as funções a desempenhar e que dependem do estado. Foram omitidos aspectos não importantes para o problema (i.e., como se fecha a porta e como se produzem os impulsos).

class WashingMachine
public class WashingMachine {
	/** Machine state. */
	private State _mode = new Open(this);

	/** closed sensor */
	public void closed() { _mode.closed(); }

	/** open button */
	public void open() { _mode.open();	}

	/** on/off button */
	public void power() { _mode.power(); }

	/** Timer (one tick per second). */
	public void tick() { _mode.tick(); }

	/**
	 * Switch mode. Note that this interface is public.
	 * @param mode the new mode.
	 */
	public void setState(State mode) {
		_mode = mode;
	}
}

A base de definição para os estado é como se indica de seguida. Contém, como se pode inferir da classe anterior, os métodos que dependem do estado da máquina (e que as subclasses de State codificam). Note-se que _machine foi definida como protected para simplificação da apresentação.

class State
public abstract class State {
	/** The washing machine.	 */
	protected WashingMachine _machine;

	/** @param machine  is the washing machine. */
	public State(WashingMachine machine) { _machine = machine; }

	/** Behaviour for closing. */
	public abstract void closed();

	/** Behaviour for open. */
	public abstract void open();

	/** Behaviour for on/off. */
	public abstract void power();

	/** Behaviour for ticking. */
	public abstract void tick();
}

As subclasses definidas para o problema cobrem as seguintes situações: porta aberta (máquina necessariamente desligada); porta fechada (máquina não iniciou lavagem ou espera terminou); máquina a lavar (5400 impulsos ou interrupção via botão on/off); espera depois da lavagem (120 impulsos).

As mensagens são apresentadas apenas por claridade e não são necessárias (excepto onde indicado no enunciado).

Note-se ainda que seria possível factorizar o código de gestão dos impulsos.

class Open
public class Open extends State {
	/**
	 * @param machine
	 */
	public Open(WashingMachine machine) {
		super(machine);
		System.out.println("** OPEN MODE: reached.");
	}

	/**
	 * @see State#closed()
	 */
	@Override
	public void closed() {
		System.out.println("** OPEN MODE: closed sensor activated.");
		System.out.println("** OPEN MODE: switching to closed/idle mode.");
		_machine.setState(new ClosedIdle(_machine));
	}

	/**
	 * @see State#open()
	 */
	@Override
	public void open() {
		System.out.println("** OPEN MODE: open button pressed (ignored).");
	}

	/**
	 * @see Washing#power()
	 */
	@Override
	public void power() {
		System.out.println("** OPEN MODE: power button pressed (ignored).");
	}

	/**
	 * @see State#tick()
	 */
	@Override
	public void tick() {
		System.out.println("** OPEN MODE: tick received (ignored).");
	}
}
class ClosedIdle
public class ClosedIdle extends State {
	/**
	 * @param machine
	 */
	public ClosedIdle(WashingMachine machine) {
		super(machine);
		System.out.println("** CLOSED/IDLE: reached.");
	}

	/**
	 * @see State#closed()
	 */
	@Override
	public void closed() {
		System.out.println("** CLOSED/IDLE MODE: closed sensor activated (ignored).");
	}

	/**
	 * @see State#open()
	 */
	@Override
	public void open() {
		System.out.println("** CLOSED/IDLE MODE: open button pressed.");
		System.out.println("** CLOSED/IDLE MODE: switching to open mode.");
		_machine.setState(new Open(_machine));
	}

	/**
	 * @see Washing#power()
	 */
	@Override
	public void power() {
		System.out.println("** CLOSED/IDLE MODE: power (on) button pressed.");
		System.out.println("** CLOSED/IDLE MODE: switching to washing mode.");
		_machine.setState(new Washing(_machine));
	}

	/**
	 * @see State#tick()
	 */
	@Override
	public void tick() {
		System.out.println("** CLOSED/IDLE MODE: tick received (ignored).");
	}
}
class Washing
public class Washing extends State {
	/**
	 * Do not stay in this state after ticking this much.
	 */
	private int MAX_TICKS = 5400;
	
	/**
	 * How many ticks have we already counted?
	 */
	private int _ticks = 0;
	
	/**
	 * @param machine
	 */
	public Washing(WashingMachine machine) {
		super(machine);
		System.out.println("** WASHING MODE: reached.");
	}

	/**
	 * @see State#closed()
	 */
	@Override
	public void closed() {
		System.out.println("** WASHING MODE: closed sensor activated (ignored).");
	}

	/**
	 * @see State#open()
	 */
	@Override
	public void open() {
		System.out.println("** WASHING MODE: open button pressed (ignored).");
	}

	/**
	 * @see State#power()
	 */
	@Override
	public void power() {
		System.out.println("** WASHING MODE: power (off) button pressed.");
		System.out.println("** WASHING MODE: switching to wait mode.");
		_machine.setState(new Wait(_machine));
	}

	/**
	 * @see State#tick()
	 */
	@Override
	public void tick() {
		System.out.println("** WASHING MODE: tick: " + _ticks);
		_ticks++;
		if (_ticks >= MAX_TICKS) {
			System.out.println("** WASHING MODE: switching to wait mode.");
			_machine.setState(new Wait(_machine));
		}
	}
}
class Wait
public class Wait extends State {
	/**
	 * Do not stay in this state after ticking this much.
	 */
	private int MAX_TICKS = 120;

	/**
	 * How many ticks have we already counted?
	 */
	private int _ticks = 0;

	/**
	 * @param machine
	 */
	public Wait(WashingMachine machine) {
		super(machine);
		System.out.println("** WAIT MODE: reached.");
	}

	/**
	 * @see State#closed()
	 */
	@Override
	public void closed() {
		System.out.println("** WAIT MODE: closed sensor activated (ignored).");
	}

	/**
	 * @see State#open()
	 */
	@Override
	public void open() {
		System.out.println("** WAIT MODE: open button pressed (ignored).");
	}

	/**
	 * @see Wait#power()
	 */
	@Override
	public void power() {
		System.out.println("** WAIT MODE: power button pressed (ignored).");
	}

	/**
	 * @see State#tick()
	 */
	@Override
	public void tick() {
		System.out.println("** WAIT MODE: tick: " + _ticks);
		_ticks++;
		if (_ticks >= MAX_TICKS) {
			System.out.println("** WAIT MODE: switching to idle mode.");
			_machine.setState(new ClosedIdle(_machine));
		}
	}
}