Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

State pattern and encapsulation

I've recently been developing a Java application and I've been attempting to follow the GoF's state pattern to attempt to tidy up the code.

The program uses a multi-agent system's agents to evaluate instructions on the behalf of a "super agent" (example below).

The super agent can exist in two states, and it's getting messy having if statements everywhere checking the state and then performing state specific behavior.

Here is a (very) simplified version of the program. The actual implementation has alot more state specific behavior.

public class superAgent
{
    //the state of the super agent
    private States state;

    //Contains information related to the operation of exampleClass. This should not be exposed through mutator methods.
    private HashMap<String, SpecificInstructionData> instructionData

    private LinkedBlockingQueue<ExampleInstruction> exampleQueue

    private final Object instructionLock = new instructionLock

    public enum States
    {
        STATE1,
        STATE2;
    }

    public void setState(state s)
    {
        state = s
    }

    //Called by a thread that continuously takes from the queue 
    private void runningThread()
    {
        while(isRunning)
        {
            synchronized(instructionLock)
            {
                ExampleInstruction ei = exampleQueue.take();
                //Add some data about the instruction into instructionData
                //send the instruction to an available agent
            }
        }
    }

    public void instructionResponseRecievedFromAgent()
    {
        if(state == States.STATE1)
        {
            doState1Behavior();
        }
        else if(state == States.STATE2)
        {
            doState2Behavior();
        }
    }

    private void doState1Behavior()
    {
        synchronized(instructionLock)
        {
            //make state specific modifications to instructionData
        }
    }

    private void doState2Behavior()
    {
        synchronized(instructionLock)
        {
            //make state specific modifications to instructionData
        }
    }
}

The state pattern would be perfect for encapsulating the behavior of the specific states into different classes according to the GoF pattern (the superAgent class would be the context). However there are two problems, both of which (IMO) break encapsulation:

  1. Most state specific behavior needs to make changes to private members (in the above example, instructionData) of the super agent. The members contain data which probably shouldn't be accessible and definitely shouldn't be mutable to wrapping classes.

  2. The state specific behavior needs to be synchronized with behavior that isn't state specific. Without exposing the lock objects (in the above example instructionLock) by either making it public or using a getter the state and context are unable to share a lock. Exposing the lock violates OOP because it could potentially be used by wrapping/extending classes.

Does anyone have any suggestions regarding how I could encapsulate this state specific behavior, bearing in mind the example and the two points above?

like image 510
AJS49 Avatar asked Oct 30 '13 17:10

AJS49


1 Answers

You can solve both problems by using Double Dispatch between the state instances and the superAgent instance to avoid breaking encapsulation.

Say you already implemented the State Pattern. instructionResponseRecievedFromAgent would look like:

public void instructionResponseRecievedFromAgent() {
  state.instructionResponseRecievedFromAgent();
}

Each State implements instructionResponseRecievedFromAgent using double dispatch this way:

abstract class State {
  abstract void instructionResponseRecievedFromAgent();
}

class State1 extends State {
  void instructionResponseRecievedFromAgent() {
    // instance variable
    agent.instructionResponseRecievedFromAgentFromState1();
  }
}

class State1 extends State {
  void instructionResponseRecievedFromAgent() {
    // instance variable
    agent.instructionResponseRecievedFromAgentFromState2();
  }
}

Doing this you let the each State specify what to do, but is the superAgent instance who decides how to do it. You have full access to the state and locks without making them public.

Finally, you implement both instructionResponseRecievedFromAgentFromState1 and instructionResponseRecievedFromAgentFromState2 in superAgent:

public void instructionResponseRecievedFromAgentFromState1() {
    //this is doState1Behavior original implementation
    synchronized(instructionLock)
    {
        //make state specific modifications to instructionData
    }
}

public void instructionResponseRecievedFromAgentFromState2() {
    //this is doState1Behavior original implementation
    synchronized(instructionLock)
    {
        //make state specific modifications to instructionData
    }
}

Keep in mind that, event though instructionResponseRecievedFromAgentFromState1 and instructionResponseRecievedFromAgentFromState2 are public methods (so the State instances can call them), they're only meant to be used only in the context of the State Pattern.

like image 53
nick2083 Avatar answered Oct 17 '22 08:10

nick2083