我有一个读取特定文件格式的应用程序。此外,该应用程序支持"插件",它可以读取替代文件格式,并将其转换为该工具本机支持的标准格式。
我想定义一个类似这样的接口:
/// <summary>
/// Interface for file transformer. Implementers of this interface provide a means to
/// convert a given file into Foo Format.
/// </summary>
public interface IFileTransformerPlugin
{
/// <summary>Gets the name of the transformer to display to the user.</summary>
/// <value>The name of the transformer displayed to the user.</value>
string DisplayName
{
get;
}
/// <summary>Determines if this transformer can handle the given format.</summary>
/// <remarks>
/// This method should return very quickly; reading only a small portion of the
/// input stream. This method is intended to be a rough cut check -- returning true
/// does not necessarily mean that the file will parse successfully on full load.
/// </remarks>
/// <param name="fileContents">The file contents.</param>
/// <returns>
/// true if this transformer can handle the supplied format, false otherwise.
/// </returns>
bool CanHandleLogFormat(Stream fileContents);
/// <summary>Transforms a file into Foo Format.</summary>
/// <param name="inputFile">The input log stream.</param>
/// <param name="outputFile">The output log stream (where the output should be
/// written).</param>
/// <returns>A transformation result which includes error information.</returns>
LogTransformationResult Transform(Stream inputFile, Stream outputFile);
}
问题来自于采用流的转换方法。从概念上讲,我希望插件主机而不是插件"拥有"这些流,并负责在这些流上调用IDispose或其他东西。例如,在测试场景中,调用方能够传递MemoryStream作为输出,然后验证输出是否有效,这将是一件好事。
然而,作为一名插件作者,希望能够使用框架中的上层格式化构造;即TextReader/TextWriter;XmlTextReader/XmlTextWriter;等等。但是这些类拥有底层流的所有权,并对底层流调用Dispose,而不管提供流的代码做什么。(至少,假设这些类本身被正确处理)
我如何重新设计这个接口来解决这个问题?这是一个可以解决的问题吗?
这调用了经典的软件工程路线:"每个问题都可以通过额外的间接级别来解决"。只需包装流并提供一个委托给基本流的线性方法。除了Dispose方法之外,什么也不做,这样无论客户端代码做什么,都可以控制流。类似:
public class OwnedStream : Stream {
private Stream stream;
public OwnedStream(Stream stream) { this.stream = stream; }
protected override void Dispose(bool disposing) {
// Do nothing
}
public override bool CanRead { get { return stream.CanRead; } }
// etcetera, just delegate to "stream"
}
请注意奖牌的另一面:你正在传递对其他代码的引用。它可以存储它,你不知道他们什么时候用完了。
我从来都不喜欢关闭读写器关闭流(这正是你描述的原因)。我通常通过传递读写器而不是流来解决这个问题。通常情况下,您无论如何都希望控制流的格式(文本与二进制),XmlTextReader/Writer可以从提供的TextReader/Writer中实例化,所以现在只需要决定是否为插件提供二进制或文本访问器。
为什么不通过接口中的getter访问流对象?然后您的应用程序可以在需要的时候从实现中获得流引用
public interface IFileTransformerPlugin {
string DisplayName { get; }
string Stream inputFile { get; }
// etc.
}