2008-12-16 5 views
4

Когда вы используете шаблон фабрики, как вы добавляете зависимости в конструкторы во время выполнения?Как реализовать инжектор конструктора на заводе?

Я создаю Foos с различными форматами - булевыми, массивными, freetext, матрицами и т. Д. Этот формат будет расти, поскольку мы найдем для Foo разные виды использования. Вот мой основной основной домен:

public interface IFoo 
{ 
    FooFormat Format { get; } 
} 

public class Foo : IFoo 
{ 
    private FooFormat _format; 

    internal Foo(FooFormat format) 
    { 
     _format = format; 
    } 

    public FooFormat Format { get { return _format; } } 
} 


public abstract class FooFormat 
{ 
} 

public class DefaultFooFormat : FooFormat 
{ 
} 

public class BooleanFooFormat : FooFormat 
{ 
    public IList<bool> Values { get; set; } 
} 

public class ArrayFooFormat : FooFormat 
{ 
    private IList<string> _values; 

    public ArrayFooFormat(IList<string> values) 
    { 
     _values = values; 
    } 

    public IList<string> Values { get { return _values; } } 
} 

IFoo оформлен в контексте потребительского:

public abstract class FooDecorator : IFoo 
{ 
    private IFoo _foo; 

    protected FooDecorator(IFoo foo) 
    { 
     _foo = foo; 
    } 

    public FooFormat Format 
    { 
     get { return _foo.Format; } 
    } 

    protected IFoo foo 
    { 
     get { return _foo; } 
    } 
} 

Я не хочу, чтобы потребитель, чтобы создать экземпляр Foo непосредственно, поэтому я заставить их использовать завод:

public abstract class FooFactory 
{ 
    protected IFoo Build<T>() 
    { 
     FooFormat format = GetFormat<T>(); 
     return new Foo(format); 
    } 

    private FooFormat GetFormat<T>() 
    { 
     if (typeof(T) == typeof(ArrayFooFormat)) return new ArrayFooFormat(new List<string>()); 
     if (typeof(T) == typeof(BooleanFooFormat)) return new BooleanFooFormat(); 
     return new DefaultFooFormat(); 
    } 
} 

И даже тогда они должны получить фабрику из моей абстрактной фабрики для их конкретного контекста.

Я специально строить FOOS в контексте HTML, например, так:

public class HtmlFoo : FooDecorator 
{ 
    public HtmlFoo(IFoo foo) : base(foo) { } 

    public string ToHtml() 
    { 
     return "<div>" + this.Format.ToString() + "</div>"; 
    } 
} 


public class HtmlFooFactory : FooFactory 
{ 
    public IFoo BuildFoo<T>() 
    { 
     IFoo foo = Build<T>(); 
     return new HtmlFoo(foo); 
    } 
} 

public class HtmlFooConsumer 
{ 
    public void DoSomeFoo() 
    { 
     var factory = new HtmlFooFactory(); 
     var htmlBooleanFoo = factory.BuildFoo<BooleanFooFormat>(); 
     var htmlArrayFoo = factory.BuildFoo<ArrayFooFormat>(); 
    } 
} 

Моя проблема заключается в моем абстрактном FooFactory: Я всегда нагнетание пустой список значений в мой ArrayFooFormat. Я хочу, чтобы иметь возможность передать в список значений от потребителя. Для других FooFormats я хочу передать правильные аргументы конструктора от потребителя. Но я хочу, чтобы публичный API был прост - мне не нужно куча перегрузок на BuildFoo().

Так как я могу передать пользовательский список значений на фабрику.BuildFoo <T>() звонок изнутри HtmlFooConsumer.DoSomeFoo()? Любые идеи, гуру stackoverflow?

+0

в таких сложных вопросах, как это действительно помогает иметь более реальный пример, а не пучок Foo-ey. – Bryan

ответ

2

Возможно, вы можете что-то сделать в этих строках, где ваш абстрактный FooFormat становится IFooFormat, а общий FooFormat предоставляет метод Init, который получает этот параметр.

Тогда одна перегрузка Build позволяет вам передать параметр.

public interface IFooFormat 
{ 
} 

public class FooFormat<TValue> : IFooFormat 
{ 
    private TValue _value; 

    public void Init(TValue value) 
    { 
     _value = value; 
    } 

    public TValue Value 
    { 
     get { return _value; } 
    } 
} 

public class ArrayFooFormat : FooFormat<IList<string>> { } 

public class BooleanFooFormat : FooFormat<bool> { } 

public class DefaultFooFormat : IFooFormat { } 

public interface IFoo { } 

public class Foo : IFoo 
{ 
    private IFooFormat _format; 

    internal Foo(IFooFormat format) 
    { 
     _format = format; 
    } 

    public IFooFormat Format { get { return _format; } } 
} 

public class FooFactory 
{ 
    protected IFoo Build<TFormat, TArg>(TArg arg) where TFormat : FooFormat<TArg>, new() 
    { 
     TFormat format = new TFormat(); 
     format.Init(arg); 
     return new Foo(format); 
    } 

    protected IFoo Build<TFormat>() where TFormat : IFooFormat, new() 
    { 
     return new Foo(new TFormat()); 
    } 
} 
+0

Отлично, Андрей. Я пробовал это, и мне нравятся результаты. Я жертвуя некоторой открываемостью в публичном API, так как потребитель должен знать, что TArg опережает время. Но я думаю, что это хороший компромисс для гибкости. – dalesmithtx

+0

Я рад, что вам понравилось:] –

0

Завод по сути является объектно-ориентированной версией статической переменной. Я бы избегал использовать один из них. Вместо того, чтобы заставлять клиентов использовать фабрику, возможно, вы можете просто вводить объекты в свои конструкторы, обойдя необходимость в фабрике.

+1

Я не уверен, что вы подразумеваете под «объектно-ориентированной версией статической переменной». Я заставляю использовать фабрику, чтобы клиенты не могли напрямую создавать Foos, но вместо этого создавали IFoos так, как они должны быть построены. Надежность на IFoo, а не Foo, приводит к инверсии зависимостей. – dalesmithtx