I'm having trouble designing a good architecture for a particular portion of my application, especially where maintaining state is involved.
I have a group of parsing operations:
My class Reader
reads in a block of data into a buffer and handles the overall control flow.
My class Parser
takes the block of data in the buffer and a ParsedDataHandler
, and divides it up into smaller chunks for the ParsedDataHandler
to manage.
ParsedDataHandler
is an interface that I am trying to design; I think I want to do something like this ("BlockInfo" represents some data structure containing various bits of state that will include a ByteBuffer holding the entire block of raw data. "ChunkMetadata" represents information including the position of each chunk within the block, and any other information the Parser
has determined about the chunk)
interface ParsedDataHandler
{
void beginBlock(BlockInfo bi);
void handleParsedData(BlockInfo bi, ChunkMetadata m);
void endBlock(BlockInfo bi);
}
so Reader
will call ParsedDataHandler.beginBlock()
to let it set up any state at the beginning of a block, and is committed to leaving a designated portion (probably all) of the BlockInfo constant until it makes a matching call to ParsedDataHandler.endBlock()
-- afterwards it might reuse the data buffer for the next block. Parser
runs through the data block and splits it into chunks according to a predefined data protocol, and will call ParsedDataHandler.handleParsedData()
multiple times, once for each chunk. One reason for the contract to include a buffer that remains fixed, is so that ParsedDataHandler
can copy the whole block of data at the beginning or end, and doesn't have to reassemble the chunks into the one block when they were there together all the time.
So there's a division of responsibility:
Reader
is just responsible for managing the overall routine and reading in the data, it couldn't care less about anything else.Parser
is the thing that splits the data block into chunks and it couldn't care less about what to do with them or how the data got thereParsedDataHandler
is the interface I'm trying to design so that concrete classes can implement it and work correctly, withoutReader
orParser
caring what it does, or without it caring about how to divide up the block into chunks or where the data came from.
My question is, should the burden on maintaining any state be on the class implementing ParsedDataHandler
and kept out of BlockInfo? And if the semantics of my interface include the fact that the raw data block in BlockInfo will not change between calls of beginBlock() and endBlock(), should I then only pass it in to beginBlock() and not to the other calls? or is it okay to send it along for convenience's sake?
Is there a better design pattern to handle a situation like this?