Passare il contesto al metodo di esecuzione nel modello di progettazione del comando

1

Passando attraverso il modello di progettazione di Command Capisco che dobbiamo creare un comando impostando il contesto tramite costruttore e chiamando il metodo execute per eseguire un'azione sul contesto. Esempio:

public class Command implements ICommand {

    Device device;

    public Command(Device device) {
        this.device = device;
    }

    public void execute() {
        this.device.turnOn()
    }
}

Mi chiedevo che con questo approccio dovessimo creare un nuovo oggetto Command per ogni oggetto device che creiamo. Sarà ok passare il contesto e alcuni parametri al metodo di esecuzione? Sto cercando qualcosa come:

public class Command implements ICommand {

    public void execute(Device device) {
        this.device.turnOn();
    }

}

Ci sono problemi con questo approccio?

    
posta Karan Khanna 18.06.2018 - 13:33
fonte

1 risposta

1

Sì, è assolutamente OK aggiungere parametri al metodo execute() . È bene ricordare come e perché viene utilizzato lo schema di comando: come una sorta di funzione lambda. In Java 8, spesso non è più necessario implementare una classe di comando concreta, perché invece possiamo utilizzare lambdas o riferimenti al metodo, supponendo che ICommand sia un @FunctionalInterface .

Un oggetto comando può salvare un certo contesto nei campi di istanza e un lambda può catturare variabili che racchiudono, ma il metodo di esecuzione può ancora prendere parametri extra e persino restituire valori! Presta attenzione a definire l'interfaccia di comando in un modo che risolva i tuoi problemi. Ciò potrebbe anche significare che l'interfaccia Command richiede più metodi, nel qual caso non può più essere un'interfaccia funzionale.

Come esempio tradizionale per lo schema di comando. potremmo avere una GUI in cui l'utente fa clic su un pulsante. Diamo al pulsante un comando da eseguire. Il pulsante non fornisce dati e non può utilizzare alcun valore di ritorno, quindi potremmo definire

@FunctionalInterface
interface ButtonCommand {
  void execute();
}

In uno scenario completamente diverso, potremmo avere un sistema di bilanciamento degli account guidato dagli eventi. Un comando riceverà lo stato del vecchio account e restituirà il nuovo stato dell'account. Quindi il nostro design potrebbe essere simile a:

@FunctionalInterface
interface AccountCommand {
  int execute(int balance);
}

class SendCommand implements AccountCommand {
  int amount;
  public SendCommand(int amount) { this.amount = amount; }
  @Override public int execute(int balance) { return balance - amount; }
}

class ReceiveCommand implements AccountCommand {
  int amount;
  public ReceiveCommand(int amount) { this.amount = amount; }
  @Override public int execute(int balance) { return balance + amount; }
}

class Account {
  int balance = 0;
  public int getBalance() { return balance; }
  public void executeCommand(AccountCommand command) {
    balance = command.execute(balance);
  } 
}

public static void main(String[] argv) {
  AccountCommand[] events = {
    new ReceiveCommand(70),
    // custom command for interest
    balance -> (int)(balance * 1.02),
    new SendCommand(30),
    new SendCommand(20),
  };
  Account acc = new Account();
  for (AccountCommand cmd : events) {
    int oldBalance = acc.getBalance();
    acc.executeCommand(cmd);
    int newBalance = acc.getBalance();
    System.out.println("Account balance changed from " + oldBalance + " to " + newBalance);
  }
}

Potrebbe essere preferibile aggiungere ulteriori metadati a questi comandi (come la controparte della transazione o l'autorizzazione per questo comando). L'account potrebbe anche tenere un registro dei comandi applicati, annullare i comandi e così via. Quindi lo schema di comando può essere più flessibile di un semplice lambda.

    
risposta data 18.06.2018 - 14:11
fonte

Leggi altre domande sui tag