2

Apologies if this is answered elsewhere; couldn't find enough information to convince myself of the best way to do this. I also realize this is a lengthy explanation with no code, but let me know if I should whip up some sample code to help demonstrate what I'm doing.

Basically:

  • implementing a communication protocol in Java using System.in/out
  • current approach is implementing a state pattern where a Scanner is instantiated on System.in in the context class
  • concrete states invoke a context method to read from the Scanner, and subsequently perform actions/transition states appropriately based on the value returned by the Scanner

My original intent for using a state pattern was to simplify the code when parsing sequences like this from System.in (don't ask about the syntax, it's something I have to work with):

  • COMMAND NAME=X
  • HEADER
  • line of header information
  • CONTENTS
  • lines of command contents
  • ENDCONTENTS
  • ENDCOMMAND

I generally define a concrete state for each type of command I expect to receive. Using the above sequence as an example, I would have a state set that looked something like {WAITING_FOR_COMMAND, COMMAND_RECEIVED, PARSING_HEADER, PARSING_CONTENTS, PARSING_DONE, COMMAND_PROCESSED}. I'd initially be in WAITING_FOR_COMMAND, then when "COMMAND NAME=X" is received I'd transition to COMMAND_RECEIVED, then when "HEADER" came in I'd transition to PARSING_HEADER, etc. This design makes traversing all the edge cases in the protocol easier, and also makes the code easy to update/maintain for when the protocol gets tweaked. Obviously much better than massive switch statements and repetitive boundary checks.

The problem I'm having is that I find myself declaring more and more state variables in the context class as I flesh out my concrete state behaviors, and know this is probably bad since I'm creating very exposed interfaces and very high linkages between the context and concrete state classes. The command sequences in this protocol can be arbitrarily long, and I need to save information imparted by each item in the command sequence until the command sequence is complete.

Using the above command sequence as an example, after "COMMAND ID=X", I want to save the value X for future use after I receive "ENDCOMMAND" and fully process the command. After "HEADER", I want to save the header information for future use after I receive "ENDCOMMAND" for when I actually process the command. So on and so forth. Simply adding commandId and header state variables to the context class works for now, but doesn't seem clean or well encapsulated to me at all.

Does anyone have any high level suggestions on how they'd approach this problem? Is there a better use of a state design pattern for this?

Just to note some ideas I've been playing with:

  • defining state contexts for each type of command sequence, and invoking the appropriate context upon receiving the relevant command from System.in; this seems almost as messy as having giant switch blocks, and seems to overly convolute the design
  • designing a full-blown FSM architecture that supports composite FSMs, where each command sequence occupies its own FSM within an overarching FSM; this seems like overkill to me
  • creating a ProtocolCommand object with various subclasses for each command sequence type; I could pass these into each state when transitioning, and gradually build them up as I go...but this clutters the state interface and forces all states to ingest a parameter that they won't necessarily use

Thanks so much! Sorry this was so verbose, let me know if I can clarify anything.

jaco0646
  • 15,303
  • 7
  • 59
  • 83
jasterm007
  • 163
  • 3
  • 9

2 Answers2

1

I have used an enum for this style of parsing with one enum for each state. An example is here http://vanillajava.blogspot.co.uk/2011/06/java-secret-using-enum-as-state-machine.html

interface Context {
    ByteBuffer buffer();
    State state();
    void state(State state);
}
interface State {
    /**
       * @return true to keep processing, false to read more data.
     */
    boolean process(Context context);
}
enum States implements State {
    XML {
        public boolean process(Context context) {
            if (context.buffer().remaining() < 16) return false;
            // read header
            if(headerComplete)
                context.state(States.ROOT);
            return true;
        }
    }, ROOT {
        public boolean process(Context context) {
            if (context.buffer().remaining() < 8) return false;
            // read root tag
            if(rootComplete)
                context.state(States.IN_ROOT);
            return true;
        }
    }
}

public void process(Context context) {
    socket.read(context.buffer());
    while(context.state().process(context));
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Thanks @Peter Lawney. Yea, that's actually exactly what I'm doing...it's just that each State basically acquires a new nugget of information that all needs to get bundled together after several State transitions. The only way I can see to make that work in this architecture is to put more state variables in Context, which is getting messy fast. I guess that might not be such a bad approach? – jasterm007 Jul 03 '12 at 19:26
  • 1
    The Context is intended to have as much or as little state as you need. Obviously the less state is simpler, but normally what gives to most clarity is important. – Peter Lawrey Jul 03 '12 at 19:28
0

I would first consider the following points :

  1. Are your state components (WAITING_FOR_COMMAND, COMMAND_RECEIVED etc )central to your application logic ? That is to say , does it really matter a lot what state the parser is in , and what state it transitions to , for the app to work correctly ? What are the values that each of the state is going to hold ? Do these values differ greatly from one state to another ? If your answers are yes , then probably you have a valid case for State pattern.But I have often seen state machines used in places where it was simply an overkill.

  2. Your use-case seems more like in need of Command pattern and an Interpreter pattern ( as it seems you are writing a grammar ).

Bhaskar
  • 7,443
  • 5
  • 39
  • 51
  • 1. In general, I'd say my answers are "no." The state pattern is really being used to assess whether a command sequence is valid, and to make it easy to implement what needs to be processed at each step in the command sequence. The application only cares what the aggregate command sequence was; a state pattern just seemed to be an easy way to error check for invalid sequences and to make the code more extensible in the future. -------------- 2. I'm fairly new to design patterns, but will look closer into those and get back to you. Thanks! – jasterm007 Jul 03 '12 at 20:36
  • Sorry it took me awhile to get back to you...I briefly looked into the other design patterns, and I don't think this relatively simple protocol really qualifies as a grammar. I ended up taking Peter Lawrey's recommendation to populate the context class as much as I needed, and have been pretty happy with the results. Thanks for the input though! – jasterm007 Jul 06 '12 at 18:55