【问题标题】:Determining which class to use at runtime确定在运行时使用哪个类
【发布时间】:2012-01-19 18:50:12
【问题描述】:

我正在处理与此非常相似的遗留代码:

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。

【问题讨论】:

    标签: c# design-patterns refactoring


    【解决方案1】:

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

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

    实际上,您让一个工厂创建了一个具体工厂,以创建您的具体解析器。

    通过这种方式,您还可以设置逻辑,在尝试创建与其关联的具体工厂之前检查程序集是否存在。

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

    【讨论】:

      【解决方案2】:

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

      但有一件事很明显:你不应该像现在这样实例化FileStream。你现在这样做的方式意味着你的解析器类可能会处理这个流,它不应该是他们的责任。此类对象应放置在创建它们的相同位置:

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

      【讨论】:

      • 关于流,我的坏例子。我只是想让演示代码尽可能简单。通常不会这样处理。
      【解决方案3】:

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

      【讨论】:

      • 很遗憾,这是一个共享库,我想避免将 Castle Windsor 作为附加依赖项引入。
      • 然后您可以创建自己的 PlugInManager,通过配置,可以解析每种类型的所有不同构造函数签名。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-11
      • 2020-09-18
      相关资源
      最近更新 更多