确定在运行时使用哪个类

本文关键字:运行时 | 更新日期: 2023-09-27 18:20:40

我正在研究与以下代码非常相似的遗留代码:

public class BaseParser
{
    public BaseParser(Stream stream) 
    {
        // Do something with stream
    }
}
public class JsonParser : BaseParser
{
    public JsonParser(Stream stream) 
        : base(stream)
    {
        // Do something
    }
}
public class XmlParser : BaseParser
{
    public XmlParser(Stream stream, string someOtherParam) 
        : base(stream)
    {
        // Do something
    }
}

我们有一个应用程序,它使用特定的解析器解析传入文件。为了确定我们需要实例化哪种类型,在工厂方法中有一个大的IF/ELSE块:

public static BaseParser Create(string type)
{
    if (type == "xml")
        return new XmlParser(new FileStream("path", FileMode.Open), "test");
    if (type == "json")
        return new JsonParser(new FileStream("path", FileMode.Open));
    // more...
    return null;
}

由于有大量的解析器驻留在一个程序集中(让我们称之为Demo.dll),我想将这些解析器中的每一个拆分为自己的程序集。"核心"类(如BaseParser)将保留在Demo.dll中,但其他解析器和任何依赖项将存在于Demo.Json.dllDemo.Xml.dll等中…

"核心"库将在运行时加载这些程序集。

string[] paths = Directory.GetFiles(Environment.CurrentDirectory, "Demo.*.dll");
foreach (string path in paths)
    Assembly.LoadFrom(path);
List<BaseParser> parsers = AppDomain.CurrentDomain
    .GetAssemblies()
    .SelectMany(x => x.GetTypes())
    .Where(x => x.IsSubclassOf(typeof(BaseParser)))
    .Select(x => (BaseParser) Activator.CreateInstance(x))
    .ToList();

问题从这里开始。上面的代码将不起作用,因为每个遗留解析器都没有无参数构造函数。此外,我想不出一个好的方法来替换IF/ELSE块,并让每个解析器确定它可以处理哪个文件。我在考虑可能在基本解析器上添加一个虚拟方法:

public virtual bool ShouldHandle(string type)
{
    return false;
}

并且每个派生的解析器将覆盖该虚拟方法。然而,有两个问题:

  1. 没有无参数构造函数,每个类的构造函数可以有不同数量的参数。这是我不能改变的,因为这些遗留类到处都在使用。

  2. 在调用ShouldHandle方法之前,我必须实例化该类。这给从构造函数中的流中读取的类带来了问题。

有没有其他方法可以将这些解析器拆分为自己的程序集?

编辑:

这是一个.NET 3.5应用程序。不幸的是没有MEF。

确定在运行时使用哪个类

我发现在这些情况下使用抽象工厂类会有所帮助。

每个程序集都有一个特定于创建驻留在该程序集中的类的工厂。

实际上,您需要一个工厂创建一个具体的工厂来创建您的具体解析器。

通过这种方式,您还可以设置逻辑,在尝试创建与其相关的混凝土工厂之前,您可以检查组件的存在。

我知道它不能绕过if else结构(尽管它可以用switch代替,为每个类传递一个参数,但它确实允许您将其分解,使其更容易处理和支持。

您可以通过无参数工厂方法或接受公共配置对象(某种属性包,或本质上是弱类型字典)的工厂方法来实例化它们。这些应该是每个插件程序集的一部分。

但有一点是显而易见的:您不应该像现在这样实例化FileStream。现在这样做意味着解析器类可能会处理这个流,而这不应该是它们的责任。这些物体应该在它们被创建的同一个地方处理:

using (var stream = new FileStream("path", FileMode.Open))
{
   var parserFactory = ParserFactoryFactory.GetFactory("xml");
   var parser = parserFactory.CreateParser(propertyBag);
   return parser.Parse(stream);
}

您是否有可能使用像CastleWindsor这样的IoC框架来处理此问题?在我看来,你描述的是一个插件系统,任何IoC框架都可以轻松处理它,而不考虑构造函数签名。