State Design Pattern

1

Mi sono imbattuto nel seguente modello di progettazione dello stato che sembra estremamente logico e semplice da implementare:

class CeilingFanPullChain
{
    private State m_current_state;

    public CeilingFanPullChain()
    {
        m_current_state = new Off();
    }
    public void set_state(State s)
    {
        m_current_state = s;
    }
    public void pull()
    {
        m_current_state.pull(this);
    }
}

interface State
{
  void pull(CeilingFanPullChain wrapper);
}

class Off implements State
{
    public void pull(CeilingFanPullChain wrapper)
    {
        wrapper.set_state(new Low());
        System.out.println("   low speed");
    }
}

class Low implements State
{
    public void pull(CeilingFanPullChain wrapper)
    {
        wrapper.set_state(new Medium());
        System.out.println("   medium speed");
    }
}

class Medium implements State
{
    public void pull(CeilingFanPullChain wrapper)
    {
        wrapper.set_state(new High());
        System.out.println("   high speed");
    }
}

class High implements State
{
    public void pull(CeilingFanPullChain wrapper)
    {
        wrapper.set_state(new Off());
        System.out.println("   turning off");
    }
}

public class StateDemo
{
    public static void main(String[] args)
    {
        CeilingFanPullChain chain = new CeilingFanPullChain();
        while (true)
        {
            System.out.print("Press ");
            get_line();
            chain.pull();
        }
    }
    static String get_line()
    {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in))
          ;
        String line = null;
        try
        {
            line = in.readLine();
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
        return line;
    }
}

La mia preoccupazione è se sia ragionevole passare l'intero riferimento CeilingFanPullChain agli oggetti stato. Mi è sempre stato insegnato a passare solo ciò che è necessario. Ad esempio, potrei desiderare di avere metodi e campi che non sono utili per gli oggetti di stato, quindi passare l'intero riferimento all'oggetto è sensato?

C'è un altro modo per riassegnare lo stato in CeilingFanPullChain, o questo non è un problema?

    
posta M-R 05.07.2016 - 00:53
fonte

2 risposte

2

Per rispondere direttamente alla domanda principale: no non è ragionevole. Perché? Perché espone il funzionamento interiore della classe.

In alternativa, considera questo:

class CeilingFanPullChain {
    private State state;

    public CeilingFanPullChain() {
        state = new Off();
    }

    private void setState(State newState) {
        state = newState;
    }

    public void pull() {
        state.pull(this);
    }

    private interface State {
        void pull(CeilingFanPullChain wrapper);
    }

    private class Off implements State {
        public void pull(CeilingFanPullChain wrapper) {
            wrapper.setState(new Low());
            System.out.println("   low speed");
        }
    }

    private class Low implements State {
        public void pull(CeilingFanPullChain wrapper) {
            wrapper.setState(new Medium());
            System.out.println("   medium speed");
        }
    }

    private class Medium implements State {
        public void pull(CeilingFanPullChain wrapper) {
            wrapper.setState(new High());
            System.out.println("   high speed");
        }
    }

    private class High implements State {
        public void pull(CeilingFanPullChain wrapper) {
            wrapper.setState(new Off());
            System.out.println("   turning off");
        }
    }
}

Nota cosa è cambiato: lo stato (e l'uso del pattern di stato stesso) è stato completamente nascosto dal codice client.

  • l'interfaccia State e tutte le implementazioni sono diventate classi interne di CeilingFanPullChain . Questo li nasconde dal client, consentendo al tempo stesso l'accesso completo ai membri privati della loro classe contenente.
  • il metodo setPattern() è privato, non visibile: i client non possono andare in giro con la macchina a stati direttamente.
  • la discussione sull'opportunità di applicare lo schema di stato o utilizzare una semplice istruzione switch diventa discutibile. Poiché è completamente incapsulato, il passaggio da uno all'altro non influirà sul client. Puoi iniziare in modo semplice, usando switch, ma man mano che la classe cresce, puoi passare all'utilizzo del pattern di stato e il client non se ne accorgerà mai.
risposta data 08.07.2016 - 00:08
fonte
1

Sebbene la risposta di RobertHarvey sia eccellente, in sostanza, il modello di stato è eccessivo per l'esempio che pubblichi e che una versione semplificata che utilizza un'istruzione switch è perfettamente accettabile e molto più concisa in questa situazione - presumo che tu stanno postulando questo come un esercizio di apprendimento specifico sull'uso del modello di stato o come proxy per un problema molto più complicato in cui lo schema dello stato è appropriato (ad esempio perché ci sono comportamenti multipli che variano a seconda dello stato e mantenerli insieme piuttosto che separandoli in diverse istruzioni switch si ottiene una migliore organizzazione del codice).

In questo caso, ci sono due possibili soluzioni per il problema che poni:

  • Potresti creare un'interfaccia per il tuo oggetto contenente lo stato che contiene solo il metodo setState() , e riceverlo nell'argomento ai metodi degli oggetti di stato

  • Potresti avere gli oggetti di stato che restituiscono un nuovo stato dai loro metodi (piuttosto che essere void come fai al momento), a quel punto non hanno bisogno di accedere allo stato a tutti.

risposta data 05.07.2016 - 09:20
fonte