Classe scheletrica / astratta solo per memorizzare lo stato condiviso?

3

Dato un'interfaccia per il modello di strategia in questo modo:

public interface Strategy {
  public Output execute(Input input);
  public Output getLastExecutionOutput();
}

se voglio memorizzare l'ultimo risultato di esecuzione di qualsiasi implementazione della strategia è corretto definire una classe astratta come questa

public abstract class AbstractStrategy implements Strategy {
  protected Output lastOutput;
  public abstract Output execute(Input input);
  public abstract Output getLastExecutionOutput();
}

quindi le implementazioni possono ereditare questo stato?

Se questo è ok, la classe astratta dovrebbe implementare anche il metodo getLastExecutionOutput, nel qual caso il campo dovrebbe diventare privato?

    
posta dabd 15.11.2014 - 07:28
fonte

1 risposta

5

IMHO puoi fare questo, e ci possono essere situazioni in cui questa è la soluzione più semplice (ad esempio, quando ti è vietato cambiare l'interfaccia in alcun modo), ma alla mia esperienza più approccio SOLID è non per rendere la parte getLastExecutionOutput dell'interfaccia della strategia. Ciò eliminerebbe del tutto la necessità della classe AbstractStrategy .

Guarda invece il luogo (tipicamente un metodo template ) dove viene chiamato execute . Probabilmente è una buona idea lasciare che il chiamante memorizzi l'ultima uscita da sola. L'intera idea del modello strategico è di separare le parti di "strategie diverse" dalle parti che sono non differenti in ogni strategia. Ciò rende più semplici gli oggetti strategici, con una migliore distribuzione delle responsabilità. L'interfaccia nel tuo esempio assegna ad ogni oggetto strategico due responsabilità per lo più indipendenti (oltre alla responsabilità di fornire il codice di strategia individuale, ha la responsabilità aggiuntiva di memorizzare nell'ultimo output in cache) e quindi implica una violazione del SRP .

Supponendo che tu non possa cambiare l'interfaccia e debba implementare i metodi precedenti: in questo caso prenderei in considerazione l'idea di implementarla in un modo leggermente diverso (non sono un ragazzo Java, quindi ti prego di perdonarmi eventuali errori sintattici):

public abstract class CachingStrategy implements Strategy {
  private Output lastOutput;
  public Output execute(Input input)
  {
       lastOutput = executeInternal(input);
       return lastOutput;
  }

  protected abstract Output executeInternal(Input input);

  public Output getLastExecutionOutput()
  {
       return lastOutput;
  }
}

Ora, quando deriva da questa classe, devi implementare executeInternal invece di execute in ogni classe figlia. La differenza notevole è che le classi figlio ora non devono più fornire il codice per impostare correttamente lastOutput , questa responsabilità è ora completamente nella classe CachingStrategy .

Come ho scritto sopra, penso che questa sia solo la seconda soluzione migliore. Quando si hanno due soluzioni quasi uguali, una con l'uso dell'ereditarietà e l'altra senza, come regola generale, provare prima a usare quella senza ereditarietà. L'utilizzo dell'ereditarietà qui non sembra essere un grosso problema a prima vista, ma pensa a cosa potrebbe accadere quando ottieni requisiti aggiuntivi "per tutte le strategie" come "attivare un evento ogni volta che viene chiamato execute", "memorizzare nella cache non solo l'ultimo output , ma anche l'ultimo input ", e così via. La semplice soluzione sembra quindi implementare tutte queste nuove funzionalità nella tua "classe base comune", che potrebbe finire in un grande ingordigia.

    
risposta data 15.11.2014 - 08:14
fonte

Leggi altre domande sui tag