Gli oggetti strategia devono avere lo stato?

4

Ho un oggetto applicazione che deve validare alcuni dati. Il particolare algoritmo di convalida non è noto fino al runtime, quindi passo un oggetto di validazione ad esso, usando il modello di strategia .

L'applicazione potrebbe aver bisogno di riutilizzare l'oggetto di validazione; ad esempio, potrebbe essere necessario eseguire due convalide contemporaneamente. Quindi idealmente, l'oggetto di validazione non conterrebbe alcun stato; sarebbe puramente un algoritmo.

Ma cosa succede se l'algoritmo di validazione di per sé ha bisogno di uno stato? Ad esempio, potrebbe essere necessario mantenere un elenco di errori di convalida.

Qual è un buon approccio in questa situazione? Sembra eccessivo creare una factory solo per questo specifico oggetto di validazione.

(Supponiamo che questo sia in una lingua come Java o PHP.)

    
posta JW01 13.06.2011 - 21:01
fonte

4 risposte

10

So di essere pedante qui, ma tutti gli oggetti possono avere lo stato. La domanda è se dovrebbero avere o meno lo stato.

La risposta a questa domanda per le strategie è la stessa della risposta per ogni altro tipo di relazione di ereditarietà: , a condizione che tu non stia violando il Principio di sostituzione di Liskov .

Il punto di una strategia (o qualsiasi altro modello di progettazione basato sull'eredità) è che al chiamante / proprietario non interessa l'implementazione. Quindi se lo stato è inizializzato internamente o tramite un costruttore di istanze e completamente autogestito, allora lo stato va bene. D'altra parte, se il mondo esterno dovrebbe essere consapevole di questo stato specifico dell'istanza, allora hai un problema.

In poche parole, se ti trovi a dover rendere questo stato pubblico , probabilmente hai un design scarso; doppiamente così se ti ritrovi a fare tipecamente dall'interfaccia. Ma lo stato privato è, beh, privato; l'intero punto di schemi come la Strategia è che al chiamante non interessa davvero.

Un'altra cosa: cerca di evitare il accoppiamento sequenziale . Anche se è OK per la strategia di avere stato, se il comportamento della strategia dipende in modo significativo su quello stato, allora ti stai aggirando in un territorio pericoloso, perché non hai idea di chi altrimenti cercheremo di utilizzare questa strategia in futuro e di non essere a conoscenza dell'ordine corretto delle operazioni, per così dire.

    
risposta data 13.06.2011 - 21:16
fonte
3

Per la domanda specifica:

Che ne dici di suddividerlo in:

  • Validator : gestisce uno o più elementi ValidationStrategy , mantiene un elenco di listener per gli errori di convalida.
  • ValidationStrategy : in realtà esegue la convalida in base alle regole
  • ValidationListener : interfaccia implementata da qualsiasi codice interessato a conoscere i guasti e raggrupparli.

Quindi la "gestione degli ascoltatori" è tenuta separata da "errori di registrazione" che è separato da "trovare errori".

Domanda più ampia: penso che il tipo di questioni statali qui.

Lo stato accettabile include elementi che consentono di evitare la creazione di una dozzina di classi ridondanti. Ad esempio:

public class SimpleStyling implements TextFormatStrategy{
    boolean bold = false;
    boolean italic = false;
    boolean underlined = false;

    /* Assume getters and setters for each */

    public String formatText(String input){
        /* ... */
    }
}

Al contrario, il tipo di stato cattivo sarebbe roba che "perde tra i lavori".

public class SimpleStyling implements TextFormatStrategy{
    MyFormat styleUsedInPreviousInvocation = ...;
}
    
risposta data 13.06.2011 - 22:13
fonte
3

Should strategy objects have state?

Il libro Gang of Four Design Patterns ha alcune cose da dire sullo stato contenuto nel modello di strategia.

Ci sono due metodi descritti per contenere lo stato:

  • Fornire alla strategia qualsiasi informazione di stato di cui ha bisogno. L'interfaccia definisce vari metodi e i parametri necessari affinché la strategia possa svolgere il suo compito.
  • Consenti al contesto di fornire se stesso alla Strategia, sia alla creazione della Strategia sia quando viene eseguita la Strategia. La strategia può accedere solo ai dati necessari attraverso l'interfaccia specificata.

La prima opzione porta a una strategia e un contesto disaccoppiati. La seconda opzione porta ad un accoppiamento più stretto tra la strategia e il contesto. Potresti considerare il fattore di accoppiamento / coesione quando scegli un'implementazione della strategia.

Strategies increase the number of objects in an application. Sometimes you can reduce this overhead by implementing strategies as stateless objects that contexts can share. Any residual state is maintained by the context, which passes it in each request to the Strategy object. Shared strategies should not maintain state across invocations. The Flyweight (195) pattern describes this approach in more detail.

Ciò che questo dice è che, tipicamente, lo stato necessario per attuare la strategia viene mantenuto nel contesto. Ciò consente di condividere le strategie tra i vari contesti senza preoccuparsi di contenere uno stato non valido. Finché il contesto ha uno stato valido, sarà garantito che lo stato valido sarà gestito dalla Strategia poiché viene fornito secondo necessità.

What's a good approach in this situation?

Nella tua situazione, prenderei in considerazione di non archiviare queste informazioni nella Strategia. Non ha nulla a che fare con l'esecuzione, ma piuttosto il risultato dell'algoritmo di validazione.

Se hai bisogno di mantenere i dati su successo / fallimento, ci sono più punti di errore, e non fermi l'algoritmo dopo il primo errore, prenderei in considerazione la seconda implementazione che ho descritto sopra, dove passi il Contesto nella strategia. Usando un callback, il Contesto mantiene una collezione di errori di validazione. Ogni volta che una strategia incontra un errore, chiama semplicemente un metodo specificato nel contesto per aggiungere informazioni.

...it might need to perform two validations simultaneously...

Per questo motivo, il mio approccio consisterebbe in una struttura di dati come una mappa nel contesto. La mappa sarebbe una mappatura tra strategia e errore di convalida, o forse una mappa tra la strategia e una raccolta di errori di convalida.

    
risposta data 14.06.2011 - 02:06
fonte
0

Prenderò in considerazione una strategia di registrazione. Dici che la tua strategia attuale deve essere possibile utilizzare in diversi algoritmi, quindi suppongo che tu intenda le istanze di detta strategia (poiché altrimenti non mi sembrerebbe importare tanto).

Ciò che prenderei in considerazione, e non so, naturalmente, se effettivamente funzionerà nella tua situazione, è di aumentare l'aspetto della registrazione. La strategia in questione, che ha bisogno di accedere, richiederebbe il log al suo proprietario. Quindi il log sarebbe unico per l'algoritmo / l'istanza del proprietario, qualunque cosa ... anche se la strategia potrebbe essere condivisa.

    
risposta data 13.06.2011 - 22:09
fonte

Leggi altre domande sui tag