假设以下情况:
我们从某个库中获取了一个Java类,该类消耗了字节流,例如一些XML解析器XmlParser1
,它公开了xmlParser1.parse(inputStream)
方法;通常,该方法在一次调用中吃掉所有字节,最终阻塞。
我们还有另一个类库,该类库来自另一个库,具有类似的功能,但实现不同:XmlParser2
和xmlParser2.parse(inputStream)
。
现在,我们想用两个解析器解析单个流。
我的第一个答案是:我们搞砸了。由于我们无法控制每个类如何使用流,因此我们所能做的就是将内存中的所有字节缓冲到一个临时文件中(或打开/重新打开它,如果可能的话)。这些消费者的API本质上是非合作的。
现在,假设我们已经控制了XmlParser1
(实现和签名),并且希望以一种更加灵活和协作的方式对其进行编码,以便调用者可以以某种合理而有效的方式来实现上述行为...您会提出什么建议?
我正在考虑的一些替代方案:
1)使XmlParser1
实现FilterInputStream
,以便当某个类(XmlParser1
)尝试从中读取某些字节时,它会在内部解析它必须执行的操作(迭代,也许有一些合理的缓冲)并还返回原始字节。 (我会说这与FilterInputStream
概念不完全对应)。这样,客户端代码可以简单地链接解析器:
public class XmlParser1 extends FilterInputStream {
public XmlParser1(InputStream rawInputStream) { ... }
public int read(byte[] b, int off, int l) throws IOException {
// this would invoke the underlying stream read, parse internall the read bytes,
// and leave them in the buffer
}
}
XmlParser1 parser1 = new XmlParser1(inputstream);
XmlParser2 parser2 = new XmlParser2(parse);
parser2.parse(); // parser2 consumes all the input stream, which causes parser1 to read an parse it too
2)而不是将
XmlParser1
视为字节的使用者,而是将其视为接收器:我们不会让它自己吃掉字节,我们会用勺子喂它。因此,我们可以不用xmlParser1.parse(inputStream)
xmlParser1.write(byte[])
...,而不是将其传递给InputStream
,而不是将其传递给OutputStream
。这将允许客户端创建一个TeeInputStream,该字节将字节透明地传递到XmlParser2
类,并同时调用XmlParser1.write()
请注意,在任何情况下我们都不需要单独的线程。
我不确定哪一个(如果有的话)在概念上更可取,是否有更好的选择。在我看来,这是一个应该已经讨论过的设计问题,但是我没有发现太多-不一定限于Java。欢迎意见和引用。
最佳答案
假设两个解析器在两个单独的线程中运行,则可能是这样的(不是工作代码)
public class Test extends FilterInputStream {
byte[] buf = new byte[8192];
int len;
Thread thread = null;
@Override
public synchronized int read(byte[] b, int off, int l) throws IOException {
while (thread == Thread.currentThread() && len > 0) {
thread.wait();
}
if (len > 0) {
System.arraycopy(buf, 0, b, off, l);
len = 0;
return l;
}
len = super.read(b, off, l);
System.arraycopy(b, off, buf, 0, len);
thread = Thread.currentThread();
notify();
return len;
}
即#1读取字节并将其保存在buf中,#1的下一次尝试被阻止,直到#2从缓冲区读取所有字节