我在为应用程序的特定部分设计良好的体系结构时遇到了麻烦,尤其是在涉及维护状态的地方。

我有一组解析操作:

我的类Reader将数据块读入缓冲区并处理整个控制流。
我的类Parser提取缓冲区中的数据块和ParsedDataHandler,并将其分成较小的块,以供ParsedDataHandler管理。

ParsedDataHandler是我要设计的接口;我想我想做这样的事情(“ BlockInfo”表示某种数据结构,其中包含各种状态位,其中包括保存整个原始数据块的ByteBuffer。“ ChunkMetadata”表示包括该块内每个块的位置的信息,以及Parser已确定的有关该块的任何其他信息)

interface ParsedDataHandler
{
    void beginBlock(BlockInfo bi);
    void handleParsedData(BlockInfo bi, ChunkMetadata m);
    void endBlock(BlockInfo bi);
}


因此Reader将调用ParsedDataHandler.beginBlock()使其在块的开头设置任何状态,并致力于保留BlockInfo的指定部分(可能是全部)常量,直到对ParsedDataHandler.endBlock()进行匹配调用为止- -之后,它可能会将数据缓冲区重新用于下一个块。 Parser贯穿数据块,并根据预定义的数据协议将其分成多个块,并将多次调用ParsedDataHandler.handleParsedData(),每个块一次。合同包含一个固定缓冲区的原因之一是,ParsedDataHandler可以在开始或结束时复制整个数据块,并且不必将大块在一起的数据重新组装成一个块。每时每刻。

因此,存在责任分工:


Reader只是负责管理整个例程并读取数据,它对其他任何事情都不关心。
Parser是将数据块拆分为多个块的东西,它不会在乎如何处理它们或数据如何到达那里
ParsedDataHandler是我要设计的接口,以便具体的类可以实现它并正常工作,而无需ReaderParser关心其功能,或者不关心如何将块分成多个块或数据来自哪里。


我的问题是,维护任何状态的负担都应该在实现ParsedDataHandler的类上并排除在BlockInfo之外吗?并且,如果接口的语义包括这样的事实,即BlockInfo中的原始数据块在beginBlock()和endBlock()的调用之间不会改变,那么我是否应该仅将其传递给beginBlock()而不传递给其他调用?还是为了方便起见一起发送?

是否有更好的设计模式来处理这种情况?

最佳答案

首先,您的实现非常接近State Pattern的经典示例。我看到的唯一问题是BlockInfo的角色。

如果在两个步骤之间更改了BlockInfo,则您需要执行您的实现。看一下我引用的Wikipedia文章。在Mousedown,MouseMove和MouseUp之间更改了Point,因此它必须是Abstract Tool的参数。

如果在两个步骤之间未更改BlockInfo,则需要考虑几件事。

如果实现ParsedDataHandler的类正在初始化BlockInfo结构的任何部分,那么我将把它分离出来并使其成为该类的私有成员,而只是让BlockInfo传入解析过程外部的初始化数据。

如果BlockInfo被BeginBlock修改并传递给后续例程,则您应该克隆BlockInfo,将其内部存储在实现ParsedDataHandler的类中。然后从参数列表中删除它。

如果以后需要BlockInfo,我将创建一个readonly属性,该属性返回内部BlockInfo。

根据您的问题,我猜您应该将BlockInfo传递到BeginBlock并将其存储在内部。从其他方法的参数中消除它,然后如果需要检索它,则添加readonly属性。

09-27 15:13