0

I'm Trying to learn about State Pattern.

In most of State Pattern examples that I have seen, methods of class change their behavior based on just one field (I mean before applying State Pattern and I'm not talking about the reference field of the state interface type) and also that field has no other use in that class and after applying the State pattern, they delete that.

but in my class, I have 2 fields (x and y) that based on their values, the print() method changes its behavior. and also I use both of these fields in some business logic methods.

This is my FunClass.

public class FunClass {
    private int x;
    private int y;

    public MyClass(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void print() {
        if (x == 1 && y == 1) {
            System.out.println("XY");
        } else if (x == 1 && y == 0) {
            System.out.println("X");
        } else if (x == 0 && y == 1) {
            System.out.println("Y");
        } else if (x == 0 && y == 0) {
            System.out.println("nothing");
        }
    }
    
    // implement some business logic.
    public int sum() {
        return x + y;
    }

    //Setters And Getters ...
}

the print() method behaves differently based on the internal state of an object (by internal state, I mean fields x and y).

so I applied the State Pattern on this class for this method:

public class FunClass {

    private int x;
    private int y;

    private State xyState;

    public FunClass(int x, int y) {
        this.x = x;
        this.y = y;

        if (x == 1 && y == 1) {
            xyState = new XY();
        } else if (x == 1 && y == 0) {
            xyState = new X();
        } else if (x == 0 && y == 1) {
            xyState = new Y();
        } else if (x == 0 && y == 0) {
            xyState = new Nothing();
        }
    }

    public void print() {
        xyState.handlePrint(); //delegate to xyState to handle the request.
    }

    // implement some business logic.
    public int sum() {
        return x + y;
    }

    public void setX(int x) {
        this.x = x;

        if (x == 1 && this.y == 1) {
            xyState = new XY();
        } else if (x == 1 && this.y == 0) {
            xyState = new X();
        } else if (x == 0 && this.y == 1) {
            xyState = new Y();
        } else if (x == 0 && this.y == 0) {
            xyState = new Nothing();
        }
    }

    public void setY(int y) {
        this.y = y;

        if (this.x == 1 && y == 1) {
            xyState = new XY();
        } else if (this.x == 1 && y == 0) {
            xyState = new X();
        } else if (this.x == 0 && y == 1) {
            xyState = new Y();
        } else if (this.x == 0 && y == 0) {
            xyState = new Nothing();
        }
    }
    public void setXyState(State xyState) {
        this.xyState = xyState;
    
        if (xyState instanceof X) {
           this.x = 1;
           this.y = 0;
        } else if (xyState instanceof Y) {
           this.x = 0;
           this.y = 1;
        } else if (xyState instanceof XY) {
           this.x = 1;
           this.y = 1;
        } else if (xyState instanceof Nothing) {
           this.x = 0;
           this.y = 0;
        }
    }

    //Setters And Getters ...
}

I declared the State interface and In FunClass (which is Context), I added a reference field of the state interface type and a public setter that allows overriding the value of that field. and then I added 4 classes (X , Y , XY , Nothing) that have implemented State interface.

But look at my constructor and my setters. I wanted the state implementation to be selected based on x and y values (values of state field and some other fields(x and y) are related And there must be consistency between their values.) and I had to add those if-else statements. And it looks like I'm back to the same problem. even worse!

What should be done now with these conditions? Is this an example of misuse of State Pattern or not? Can we apply the State pattern at the same time and get rid of the conditions?

PS: My problem is not about reducing the number of codes or having a single-method class with only one instance and my main issue is the State pattern. the problem is that we applied state pattern but we have situation like this. and these are my questions:

  1. Is this an example of misuse of State Pattern or not?
  2. Can we apply the State pattern at the same time and get rid of these conditional statements?
3
  • No need to use the State pattern here. You can think of a function as of a single-method class, with only one instance created for you in memory. So, if you extracted each of your System.out.println("..."); calls into a separate function, that's like having 4 State instances, and your print method is then just orchestrating the state transitions. So, if it's only the print method that changes behavior based on state, the State pattern is not very useful. 1/3 Oct 17, 2021 at 8:19
  • However, if you had a number of other methods that had very similar if-conditionals (or switch statements), then the State pattern enables you to avoid having to keep track of all that, and have separate objects that focus only on a particular state, and provide behavior for each method only for that state. The context keeps all of the states in memory, and just focuses on state transition logic, delegating all other state-dependent operations to the current state instance. Transitions would happen when you set x or y. 2/3 Oct 17, 2021 at 8:19
  • You say that you also use these fields in some other business logic methods; if you are making decisions based on their values, see if you can pull some of that logic back into you FunClass, so that your business methods don't have to check the x & y directly, but can just "ask" the FunClass object for some kind of an indicator (bool, enum) that lets them know how to proceed, or pass in a Consumer that FunClass object can call (this looks like Java?). Then your FunClass might have several state-dependent methods, and the State pattern will make more sense. 3/3 Oct 17, 2021 at 8:19

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.