Introducció al Patró Command

El patró Command és un patró de disseny de comportament que converteix una sol·licitud en un objecte autònom que conté tota la informació sobre la sol·licitud. Aquest patró permet parametritzar els mètodes amb sol·licituds, posar sol·licituds en cua o registrar sol·licituds, i suportar operacions desfer.

Objectius del Patró Command

  • Encapsulació de sol·licituds: Permet encapsular una sol·licitud com un objecte, permetent així que es puguin passar sol·licituds com a arguments de mètodes.
  • Desacoblament: Desacobla l'objecte que invoca l'operació de l'objecte que realitza l'operació.
  • Historial i desfer: Permet mantenir un historial de sol·licituds i possibilita la funcionalitat de desfer.

Components del Patró Command

  1. Command: Interfície que declara un mètode per executar una operació.
  2. ConcreteCommand: Implementa la interfície Command i defineix la relació entre el receptor i una acció.
  3. Client: Crea un objecte ConcreteCommand i estableix el seu receptor.
  4. Invoker: Demana a l'objecte Command que executi la sol·licitud.
  5. Receiver: Sap com dur a terme les operacions associades a la sol·licitud.

Diagrama UML del Patró Command

+----------------+      +----------------+      +----------------+
|    Client      |      |    Invoker     |      |   Receiver     |
+----------------+      +----------------+      +----------------+
        |                       |                       |
        |                       |                       |
        |                       |                       |
        v                       v                       v
+----------------+      +----------------+      +----------------+
|    Command     |<-----| ConcreteCommand|----->|   Receiver     |
+----------------+      +----------------+      +----------------+
|+ execute()     |      |+ execute()     |      |+ action()      |
+----------------+      +----------------+      +----------------+

Exemple Pràctic

Escenari

Suposem que estem desenvolupant una aplicació de control remot per a dispositius electrònics. Volem encapsular les sol·licituds d'encesa i apagat dels dispositius en objectes de comanda.

Implementació en Java

Interfície Command

public interface Command {
    void execute();
}

Classe Receiver

public class Light {
    public void turnOn() {
        System.out.println("The light is on");
    }

    public void turnOff() {
        System.out.println("The light is off");
    }
}

Classes ConcreteCommand

public class TurnOnLightCommand implements Command {
    private Light light;

    public TurnOnLightCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOn();
    }
}

public class TurnOffLightCommand implements Command {
    private Light light;

    public TurnOffLightCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOff();
    }
}

Classe Invoker

public class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}

Classe Client

public class Client {
    public static void main(String[] args) {
        Light light = new Light();
        Command turnOn = new TurnOnLightCommand(light);
        Command turnOff = new TurnOffLightCommand(light);

        RemoteControl remote = new RemoteControl();
        remote.setCommand(turnOn);
        remote.pressButton();

        remote.setCommand(turnOff);
        remote.pressButton();
    }
}

Explicació del Codi

  1. Interfície Command: Defineix el mètode execute() que serà implementat per les comandes concretes.
  2. Receiver (Light): Conté les operacions que es poden executar (encendre i apagar la llum).
  3. ConcreteCommand (TurnOnLightCommand i TurnOffLightCommand): Implementen la interfície Command i encapsulen les sol·licituds per encendre i apagar la llum.
  4. Invoker (RemoteControl): Manté una referència a un objecte Command i invoca el mètode execute() quan es prem el botó.
  5. Client: Configura els objectes Command i Receiver, i passa les comandes a l'Invoker per a la seva execució.

Exercici Pràctic

Enunciat

Implementa un sistema de control remot per a un ventilador que pugui encendre, apagar i ajustar la velocitat del ventilador. Utilitza el patró Command per encapsular aquestes sol·licituds.

Solució Proposada

Interfície Command

public interface Command {
    void execute();
}

Classe Receiver

public class Fan {
    public void turnOn() {
        System.out.println("The fan is on");
    }

    public void turnOff() {
        System.out.println("The fan is off");
    }

    public void setSpeed(int speed) {
        System.out.println("The fan speed is set to " + speed);
    }
}

Classes ConcreteCommand

public class TurnOnFanCommand implements Command {
    private Fan fan;

    public TurnOnFanCommand(Fan fan) {
        this.fan = fan;
    }

    @Override
    public void execute() {
        fan.turnOn();
    }
}

public class TurnOffFanCommand implements Command {
    private Fan fan;

    public TurnOffFanCommand(Fan fan) {
        this.fan = fan;
    }

    @Override
    public void execute() {
        fan.turnOff();
    }
}

public class SetFanSpeedCommand implements Command {
    private Fan fan;
    private int speed;

    public SetFanSpeedCommand(Fan fan, int speed) {
        this.fan = fan;
        this.speed = speed;
    }

    @Override
    public void execute() {
        fan.setSpeed(speed);
    }
}

Classe Invoker

public class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}

Classe Client

public class Client {
    public static void main(String[] args) {
        Fan fan = new Fan();
        Command turnOn = new TurnOnFanCommand(fan);
        Command turnOff = new TurnOffFanCommand(fan);
        Command setSpeed = new SetFanSpeedCommand(fan, 3);

        RemoteControl remote = new RemoteControl();
        remote.setCommand(turnOn);
        remote.pressButton();

        remote.setCommand(setSpeed);
        remote.pressButton();

        remote.setCommand(turnOff);
        remote.pressButton();
    }
}

Errors Comuns i Consells

  • No encapsular tota la informació necessària: Assegura't que els objectes Command encapsulin tota la informació necessària per executar la sol·licitud.
  • No desacoblar correctament: Recorda que l'objectiu principal del patró Command és desacoblar l'emissor de la sol·licitud del receptor de la sol·licitud.
  • No implementar la interfície Command: Totes les comandes concretes han d'implementar la interfície Command.

Resum

El patró Command és una eina poderosa per encapsular sol·licituds com a objectes, permetent una major flexibilitat i desacoblament en el disseny del programari. Aquest patró és especialment útil per implementar funcionalitats com desfer, historial de sol·licituds i cues de sol·licituds. Amb una comprensió sòlida del patró Command, els desenvolupadors poden crear sistemes més modulars i mantenibles.

© Copyright 2024. Tots els drets reservats