2010-01-20 25 views
6

La mia attuale comprensione del modello di progettazione dello stato è fondamentalmente questa:Qual è il modo migliore, usando lo schema di progettazione "Stato", per cambiare stato?

Incapsulare tutto il comportamento di un oggetto in uno stato particolare in un oggetto. Delegare le richieste all'oggetto di stato "Attuale".

La mia domanda è: qual è il modo migliore per gestire le transizioni di stato? Nel mio caso, è probabile che l'oggetto stato "Attuale" sarà quello che decide su quale altro stato dobbiamo passare. Ho pensato a 2 modi per implementarlo:

1) I metodi degli oggetti di stato possono restituire un valore particolare che significa "Sto richiedendo una transizione di stato". L'oggetto principale può quindi interrogare lo stato corrente per quale nuovo stato dovremmo passare, chiamare "ChangeState()", quindi instradare la richiesta originale al nuovo stato.

2) L'oggetto stato stesso può chiamare "ChangeState()" sul padre e quindi passare la richiesta che ha causato la modifica dello stato sul nuovo oggetto.

Lo scenario 2 presenta il vantaggio che l'oggetto principale deve sempre solo delegare le richieste allo stato "Corrente" (gestirà internamente tutte le transizioni di stato necessarie). È anche forse un po 'meno ovvio.

Spero ci siano modi migliori per gestire questo scenario. Cosa ne pensi?

+0

Uno dei tag è scritto in modo errato. Non riesco a correggerlo perché non ho abbastanza rep. –

+0

Come hai detto che lo stato 'corrente' arriva a decidere quale sarà il prossimo stato, allora il secondo approccio non è solo un approccio migliore ma anche il modo giusto per farlo. – Anurag

+0

FYI: Design Patterns Mindmap: http://www.mindmeister.com/7008138/design-patterns – jldupont

risposta

2

Penso che tu sia te stesso, forse limitarsi a pensare solo in termini di oggetti che implementano il modello di stato (oggetto di contesto e gli oggetti dello Stato). Questo non è il caso, e ci sono altri oggetti coinvolti (clienti). È possibile che il client, che detiene un riferimento all'oggetto contesto, debba avere la responsabilità dello stato di transizione.

considerare questo composto da esempio:

// Paintbrush is the context object 
class Paintbrush { 
    // The State object, ColourState would be the abstraction 
    private ColourState colourState; 

    // ... other class stuff 

    public paint() { 
     // Delegation to the state object 
     this.colourState.paintInYourSpecificColour(); 
    } 

    public void setColourState(ColourState newState) { 
     this.colourState = newState; 
    } 
} 

Questo dovrebbe essere sufficiente implementazione per l'oggetto contesto. Si noti che né la classe né la classe hanno alcuna conoscenza delle transizioni di stato. Questo per ridurre il numero di responsabilità e per offrire un design più flessibile.

In sostanza, i cambiamenti di stato potrebbero essere la responsabilità della classe chiamante. Il modo in cui questo è effettivamente realizzato nel codice è il dettaglio dell'implementazione, ma la cosa importante da notare è che né l'oggetto contesto né l'oggetto stato hanno una responsabilità per lo stato di transizione.

Sto cercando di assicurarmi di non utilizzare un argomento Strawman, ma continuerò a correre con l'esempio. Dicono in diversi punti si voleva dipingere modelli differenti, ed i colori utilizzati devono essere in un ordine specifico, il vostro cliente sarebbe decidere quando cambiare stato, in questo modo:

public void paintRainbow() { 
    paintbrush.setColourState(new RedColourState()); 
    // do painting... 
    // Change state to next colour 
    paintbrush.setColourState(new OrangeColourState()); 
    // Chane state again, and so on... 
} 

È potrebbero avere l'ordine dei colori specificato dallo stato o dall'oggetto contesto, es. avere una sottoclasse di Paintbrush chiamata RainbowPaintbrush e selezionerebbe il colore successivo.Oppure i tuoi oggetti di stato potrebbero scegliere lo stato successivo, nel qual caso dovresti avere uno RedRainbowColourState, che sapeva che lo stato successivo era e così via. Ma il problema con entrambi questi esempi è che devi entrare e modificare (per estensione) sia il contesto che gli oggetti di stato per ottenere un diverso insieme di transizioni. Tuttavia, se nessuno dei due conosce le transizioni e questa è la responsabilità della classe chiamante, ciò può essere fatto senza modificare lo stato o l'oggetto contesto. Vale a dire.

public void paintChessboard() { 
    paintbrush.setColourState(blackColourState); 
    // do painting... 
    // change state 
    paintbrush.setColourState(whiteColourState); 
    // etc... 
} 

Questo è un esempio semplificato, ma generalmente vale.

Una lettura veloce di example of the State pattern di Wikipedia mostra che i singoli stati conoscono lo stato successivo, quindi non penso che sia invalido. Immagino che in generale sia un compromesso su dove vuoi che il controllo sia, e su come questo si adatta al tuo problema. Questo è il modo utilizzando gli oggetti di stato concreti per la transizione possa inserirsi nel mio esempio zoppo:

public class RedRainbowColourState implements ColourState { 
    public void doPaint(Paintbrush paintbrush) { 
     // do painting 
     ColourState nextStateInRainbow = new OrangePaintbrushColourState(); 
     paintbrush.setColourState(nextStateInRainbow); 
    } 

Ma si noti l'esplosione di classi statali che saranno necessari per la transizione attraverso tutti gli stati che utilizzano questo modo. Tuttavia, un vantaggio qui è che il cliente può essere sollevato dalla responsabilità e dalla conoscenza di come creare i singoli stati. Nella tua situazione, quello potrebbe essere il modo migliore di transizione.


Per riepilogare, è possibile consentire ai singoli stati di eseguire la transizione o anche l'oggetto contesto. Un'altra scelta è lasciare che il client gestisca le transizioni di stato.

3

Preferisco che il metodo di stato restituisca un nuovo oggetto di stato (riduce l'accoppiamento e più appropriato ai principi di S.O.L.I.D.).

Qui esempio (questa idea utilizza nel progetto reale):

class ExternalContext { 
    //... 
} 

class Entity 
{ 
    public Entity(ExternalContext context) 
    { 
     //Creating current state with factory method 
     state = EntityState.Create(context); 
    } 

    public void ChangeEntity(ExternalContext context) 
    { 
     state = state.Change(context); 
    } 

    private EntityState state; 
} 

abstract class EntityState 
{ 
    public abstract EntityState Change(ExternalContext externalContext); 
    public static EntityState Create(ExternalContext externalContext); 
} 
class EntityState1 : EntityState { 
    public override EntityState Change(ExternalContext externalContext) { 
     //.. 
    } 
} 
+2

Puoi spiegare come questo è più allineato ai principi SOLID e riduce l'accoppiamento? Grazie – Deepak

Problemi correlati