创建对象族取决于字符串或枚举违反Open - Closed原则

时间:2015-02-27 07:54:53

标签: c# design-patterns

我正在开发一个库,将HTML文档转换为word文档。这是通过遍历HTML文档并逐个处理HTML元素来完成的。有一系列类可以处理每个HTML标记。

public abstract class DocxElement
{
   public void Process();
}

public class DocxTable : DocxElement
{
   public override void Process(){}
}

public class DocxDiv : DocxElement
{
  public override void Process(){}
}

上述类负责处理其html对应项。因此,每当我扩展库以支持其他html标记时,我将只从DocxElement创建一个子类。 html解析器使用工厂类在遇到HTML标记时生成隐含的DocxElement类。

public class ElementFactory
{

  public DocxElement Resolve(string htmlTag)
  {
     switch(htmlTag)
     {
        case "table":
         return new DocxTable();

        case "div":
         return new DocxDiv();
     }
  }
}

现在我觉得它违反了开放封闭原则。我不想仅仅因为设计模式需要使用反射。所以我创建了一个单例字典来注册元素类。

Dictionary<string, Func<DocxElement>> doc;

doc.Add("table",()=>{ new DocxTable();});

最后我能够消除switch语句。当我创建一个新的子类时,我还需要在字典中添加元素。

有没有更好的方法来做到这一点?请指教。

1 个答案:

答案 0 :(得分:1)

我会说你的Dictionary方法很好。尝试使这个泛型的任何其他东西将失去静态编译时间检查。如果您准备牺牲编译时间检查,可以使用反射来使此代码通用。

public class ElementFactory
{
    public DocxElement Resolve(string htmlTag)
    {
        var type = Type.GetType(string.Format("{0}.Docx{1}",
            typeof(ElementFactory).Namespace,
            CultureInfo.CurrentCulture.TextInfo.ToTitleCase(htmlTag)));
        return (DocxElement)Activator.CreateInstance(type);
    }
}

使用方法:

ElementFactory factory = new ElementFactory();
var table = factory.Resolve("table");//Works
var div = factory.Resolve("div");//Works
var span = factory.Resolve("span");//Explodes!!

正如您所看到的,由于多种原因,这可能会导致运行时失败。找不到类型,找到类型,但没有公共无参数构造函数,找到类型,但它不是从DocxElement等派生出来的。

因此,最好能坚持使用Dictionary选项IMO。