我怎样才能将其转换为工厂/抽象工厂?

时间:2013-10-17 13:46:56

标签: c# generics design-patterns migradoc

我正在使用MigraDoc来创建pdf文档。

我的商业实体与MigraDoc中使用的商业实体类似。

    public class Page{
      public List<PageContent> Content { get; set; } 
    }


  public abstract class PageContent {

        public int Width { get; set; }      
        public int Height { get; set; } 
        public Margin Margin { get; set; }
    }

    public class Paragraph : PageContent{
         public string Text { get; set; }    
    }

    public class Table : PageContent{
         public int Rows { get; set; }    
         public int Columns { get; set; }  
        //.... more
    }

在我的业务逻辑中,每种类型都有渲染类

public interface IPdfRenderer<T>
{
    T Render(MigraDoc.DocumentObjectModel.Section s);
} 

class ParagraphRenderer
    : IPdfRenderer<MigraDoc.DocumentObjectModel.Paragraph>
{
    BusinessEntities.PDF.Paragraph paragraph;

    public ParagraphRenderer(BusinessEntities.PDF.Paragraph p)
    {
     paragraph = p;
    }

    public MigraDoc.DocumentObjectModel.Paragraph Render(MigraDoc.DocumentObjectModel.Section s)
    {
        var paragraph = s.AddParagraph(); // add text from paragraph etc
        return paragraph;
    }
}


public class TableRenderer : IPdfRenderer<MigraDoc.DocumentObjectModel.Tables.Table>
{
    BusinessEntities.PDF.Table table;
    public TableRenderer(BusinessEntities.PDF.Table t)
    {
        table =t;
    }
    public MigraDoc.DocumentObjectModel.Tables.Table Render(Section obj)
    {
        var table = obj.AddTable();
        //fill table based on table
    }
}

我想创建一个PDF页面:

            var document = new Document();
            var section = document.AddSection();// section is a page in pdf
            var page = GetPage(1); // get a page from business classes
            foreach (var content in page.Content)
            {
                      //var renderer = createRenderer(content); //
                     // get Renderer based on Business type ??
                     // renderer.Render(section) 
            }

对于createRenderer(),我可以使用switch case / dictionary和return type。

如何根据类型一般获取/创建渲染器?

我如何在这里使用工厂或抽象工厂?

或者哪种设计模式更适合这个问题?

2 个答案:

答案 0 :(得分:1)

您可以使用您的密钥类型的工厂,this文章有一些示例。

如果您能够负担得起使用IoC,如果您让IoC引擎根据字符串决定应该使用哪个具体类,那么您应该相应地配置容器。该字符串可以是Type name。 我建议ninject但是像windsor这样的任何其他IoC都可以工作。

下面是一个如何使用具有通用接口的工厂完成的示例,因为在您的情况下,您需要传递的是构造函数。

public class Factory<T>
{
    public Factory()
    {
        _Mappings = new Dictionary<string,Func<IInterface<T>>>(2);

        _Mappings.Add("MyClass1", () => new MyClass1() as IInterface<T>);
        _Mappings.Add("MyClass2", () => new MyClass2() as IInterface<T>);
    }
    public IInterface<T> Get(string typeName)
    {
        Func<IInterface<T>> func;
        if (_Mappings.TryGetValue(typeName, out func))
        {
            return func();
        }
        else
            throw new NotImplementedException();
    }

    readonly Dictionary<string, Func<IInterface<T>>> _Mappings;
}

public class MyClass1 : IInterface<int>
{
    public int Method()
    {
        throw new NotImplementedException();
    }
}

public class MyClass2 : IInterface<string>
{
    public string Method()
    {
        throw new NotImplementedException();
    }
}

public interface IInterface<T>
{
    T Method();
}

我仍然认为IoC更好,但这是一种有效的方法。

答案 1 :(得分:1)

您的界面定义可以增强。而只是将通用和接受输入作为构造函数返回,您可以同时接受泛型作为输入并返回它。

public interface IPdfRenderer<T>
{
    T Render(MigraDoc.DocumentObjectModel.Section s, T baseContent);
}

然后扩大arutromonriv的答案,

public class DynamicRenderer : IPdfRenderer<PageContent>
{
    public DynamicRenderer(IPdfRenderer<Paragraph> paragraph
        , IPdfRenderer<Table> table){
        //variable assignment
    }
    public PageContent Render(MigraDoc.DocumentObjectModel.Section s, PageContent baseContent){
        if(baseContent is Paragraph){ return paragraph.Render(s, baseContent as Paragraph); }
        else{ return table.Render(s, baseContent as Table); }
    }
}

你可以这样做:

var renderer = createRenderer(); //
foreach (PageContent content in page.Content)
{
    // get Renderer based on Business type ??
    renderer.Render(section, content);
}