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.