SpazioCodice

Codice senza IF: (Ancora una volta) lo State Pattern

Codice senza IF: (Ancora una volta) lo State Pattern

Assegnazione: Loop su una Lista di stringhe e stampare ogni elemento seguendo le regole seguenti:
  • il primo valore deve essere stampato così com’è
  • gli altri elementi devono essere stampati in maiuscolo
  • alla fine del ciclo stampare “Exit”

Una prima versione

				
					public class Service {
  public void loop(List strings) {
    int index = 0;
    for (String string : strings) {
      if (index == 0) {
        System.out.println(string);
      } else {
        System.out.println(string.toUpperCase());
      }
      index++;
    }
    System.out.println("Exit");
  }
}
				
			

Ok, funziona, è concisa e chiara; proviamo a “complicare” un po’ le cose.

State Pattern

I task eseguiti a ogni iterazione nell’esempio sono banali, ma in uno scenario reale potrebbero essere complessi: perché non isolare questi task in un metodo separato o addirittura in una classe separata? Introduciamo quindi un’interfaccia Iteration che rappresenta il task eseguito in una determinata iterazione.
				
					interface Iteration {
  void execute(String value);
}
				
			

Tre task, tre implementazioni iniziali:

				
					Iteration firstIteration = new Iteration() {
  public void execute(String value) {
    System.out.println(value);
  }
}


Iteration middleIteration = new Iteration() {
  public void execute(String value) {
    System.out.println(value.toUppercase());
  }
}


private final Iteration noIteration = new Iteration() {
  public void execute() {
    System.out.println("Exit");
  }
}
				
			
Ora, ogni task dovrebbe essere responsabile dell’esecuzione della propria logica e, inoltre, dovrebbe fornire un meccanismo per passare all’iterazione successiva. Un modo possibile per farlo è introdurre una piccola modifica all’interfaccia Iteration e alla classe esterna che gestisce i task:
				
					interface Iteration {
  void execute();
}

public class Loop {
  private final Iterator iterator;

  ...

  public Loop(final List strings) {
   iterator = strings.iterator();
  }

  Iteration firstIteration = new Iteration() {
    public void execute() {
      System.out.println(iterator.next());
    }
  }

  Iteration middleIteration = new Iteration() {
    public void execute() {
      System.out.println(iterator.next().toUppercase());
    }
  }

  Iteration lastIteration = new Iteration() {
    public void execute() {
      System.out.println("Exit");
    }
  }
}
				
			
A questo punto dobbiamo mantenere un riferimento all’iterazione corrente. Introduciamo quindi un altro membro di istanza: una currentIteration inizialmente impostata su firstIteration.
				
					private Iteration currentIteration = firstIteration;
				
			
Due cose mancano ancora. Primo, la classe Loop deve avere un metodo per avviare l’esecuzione del ciclo:
				
					public void start() {
  currentIteration.execute();
}
				
			
E poi, ogni task deve decidere come proseguire con l’iterazione successiva. Questa logica può essere isolata in un composed method:
				
					private void nextIteration() {
  currentIteration = iterator.hasNext() 
                        ? middleIteration
                        : noIteration;
  currentIteration.execute();
}
				
			
Lo so: si tratta ancora di una logica condizionale; tuttavia, non è esattamente la stessa della prima versione di Loop: non ci sono indici né variabili temporanee e ogni classe Iteration contiene esattamente ciò che deve essere eseguito in un determinato ciclo di iterazione. Il codice in questo esempio è solo un esercizio, e sono d’accordo con te che i benefici non sono così evidenti; questo perché il problema di partenza è molto semplice. Questa è la versione finale della classe Loop:
				
					interface Iteration {
  void execute();
}

public class Loop {
  Iteration firstIteration = new Iteration() {
    public void execute() {
      System.out.println(iterator.next());
      nextIteration();
    }
  }

  Iteration middleIteration = new Iteration() {
    public void execute() {
      System.out.println(iterator.next().toUppercase());
      nextIteration();
    }
  }

  Iteration lastIteration = new Iteration() {
    public void execute() {
      System.out.println("Exit");
    }
  }

  private final Iterator iterator;
  private Iteration currentIteration = firstIteration;

  public Loop(final List strings) {
   iterator = strings.iterator();
  }

  public void start() {
    currentIteration.execute();
  }

  private void nextIteration() {
    currentIteration = iterator.hasNext() 
                        ? middleIteration
                        : noIteration;
    currentIteration.execute();
  }
}
				
			

Share this post

Rispondi

Scopri di più da SpazioCodice

Abbonati ora per continuare a leggere e avere accesso all'archivio completo.

Continua a leggere