Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is this the name of this Java state-based design pattern?

At my work, we have surveys, and one survey involves multiple steps. I work in automation, so I design tests around the page-objects we create for these surveys. We call this particular survey a "flow" survey because it has multiple steps. So you can skip step1 (survey A), then complete or skip step 2 (survey B), then complete or skip step 3 (survey C). Naively, we could write a test that just has methods that look like this:

public void completeSurveyA() {
    //...
}
public void skipSurveyB() {
    //...
}
public void completeSurveyB() {
    //...
}
public void skipSurveyC() { 
    //...
}
public void completeSurveyC() {
    //...
}

You would use it like this

completeSurveyA();
skipSurveyB();
completeSurveyC();

However, that could be a problem because we might call completeSurveyB() before we call completeSurveyA(), call completeSurveyA twice, etc. and the test would break. To avoid this, I introduced a different approach where calling a method on surveyA would return a surveyB object, which would return a surveyC object.

public class SurveyFlow() {
    public SurveyB completeSurveyA() {
        //...
        return new SurveyB();
    }
    private class SurveyB() {
        public SurveyC skipSurveyB() {
            //...
            return new SurveyC();
        }
        public SurveyC completeSurveyB() {
            //...
            return new SurveyC();
        }
        private class SurveyC() {
            public void skipSurveyC() {
                //...
            }
            public void completeSurveyC() {
                //...
            }
        }
    }
}

You would use it like this

new SurveyFlow().completeSurveyA().skipSurveryB().completeSurveyC();

The pattern reminds me of a state machine because only certain methods are available to you in different states, but I'm wondering if there is a more specific name for this pattern.

like image 369
michaelsnowden Avatar asked Jul 30 '15 20:07

michaelsnowden


2 Answers

According to the classes of your example, it's a FluentInterface:

Probably the most important thing to notice about this style is that the intent is to do something along the lines of an internal DomainSpecificLanguage. (...) The API is primarily designed to be readable and to flow.

It's not the builder pattern, because you're not building anything (i.e. you don't have a final build() method where data gathered in previous steps is used to create an instance).

It's not the state pattern either, because operations (skip() and complete() in this case) do not depend on the state of an object (actually steps don't have a state).

It would have been the state pattern if the whole survey had been modeled as an object with one method whose implementation depended on different states (in this case, the states would be the steps plus the action taken, i.e. surveyACompleted, surveyASkipped, surveyBCompleted, surveyBSkipped, etc, while the method would be something like nextStep()):

public class SurveyFlow {

    private SurveyState state; // this represents the current step

    public SurveyFlow(boolean skipFirst) {
        this.state = skipFirst ? new SurveyASkipped() : new SurveyACompleted();
    }

    void setState(SurveyState state) {
        this.state = state;
    }

    public void takeStep(boolean skipNext) { // takeStep operation delegated 
                                             // to the state (current step)
        this.state.takeStep(skipNext, this); // "this" passed to the step so 
                                             // that it can switch to the 
                                             // next step if needed
    }
}

The state would be polymorphically represented by each step of the SurveyFlow:

abstract class SurveyState {

    protected abstract void takeStep(boolean skipNext, SurveyFlow survey);
}

Survey A states would be as follows:

class SurveyACompleted extends SurveyState {

    protected void takeStep(boolean skipNext, SurveyFlow survey) {
        // ...

        survey.setState(skipNext ? new SurveyBSkipped() : new SurveyBCompleted());
    }
}

class SurveyASkipped extends SurveyState {

    protected void takeStep(boolean skipNext, SurveyFlow survey) {
        // ...

        survey.setState(skipNext ? new SurveyBSkipped() : new SurveyBCompleted());
    }
}

Survey B states would be as follows:

class SurveyBCompleted extends SurveyState {

    protected void takeStep(boolean skipNext, SurveyFlow survey) {
        // ...

        survey.setState(skipNext ? new SurveyCSkipped() : new SurveyCCompleted());
    }
}

class SurveyBSkipped extends SurveyState {

    protected void takeStep(boolean skipNext, SurveyFlow survey) {
        // ...

        survey.setState(skipNext ? new SurveyCSkipped() : new SurveyCCompleted());
    }
}

For your example:

  1. Complete Survey A
  2. Skip Survey B
  3. Complete Survey C

You could do:

SurveyFlow survey = new SurveyFlow(false); // will complete survey A
survey.takeStep(true);                     // completed survey A and will skip survey B
survey.takeStep(false);                    // skipped survey A and will complete survey C
survey.takeStep(true);                     // completed survey C

If survey C is the last step, then it can ignore the boolean argument and shouldn't set further steps.

like image 121
fps Avatar answered Nov 15 '22 00:11

fps


This is in a way the State pattern, but does not completely adhere to the State pattern described by GoF, because you are not changing the state of a single object, but rather creating and returning a new object of different class which you use afterwards.

Actually, this resembles much more the Builder pattern, where the completeSurveyC() acts as a build or getResult method to build a Surway from multiple consisting pieces specified earlier.

like image 27
Dragan Bozanovic Avatar answered Nov 14 '22 23:11

Dragan Bozanovic