views:

199

answers:

1

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 there
  • ParsedDataHandler is the interface I'm trying to design so that concrete classes can implement it and work correctly, without Reader or Parser 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?

+2  A: 

First your implementation is pretty close to the the classic example of a State Pattern. The only issue I see is the role of BlockInfo.

If BlockInfo is being changed in between the steps then your implementation is what you need to do. Look at the Wikipedia article I referenced. Point is being changed between Mousedown, MouseMove, and MouseUp so it has to be a parameter of a Abstract Tool.

If BlockInfo is not being changed between the steps then you have several things to consider.

If the class implementing ParsedDataHandler is initializing any part of the BlockInfo structure then I would separate that out and make it a private member of the class and just have BlockInfo pass in the initialization data that is external to the parsing process.

If BlockInfo is being modified by BeginBlock and passed to the subsequent routines then you should clone BlockInfo, store it internally in the class implementing ParsedDataHandler. Then eliminate it from the parameter list.

If you need BlockInfo afterward I would make a readonly property that return the internal BlockInfo.

Based on your question my guess you should pass BlockInfo into BeginBlock and store it internally. Eliminate it from the parameters from the other methods then add the readonly property if you need to retrieve it.

RS Conley