【问题标题】:How do you implement constructor injection in a factory?如何在工厂中实现构造函数注入?
【发布时间】:2008-12-16 23:29:36
【问题描述】:

当您使用工厂模式时,如何在运行时将依赖项注入构造函数?

我正在构建具有不同格式的 Foos - boolean、array、freetext、matrix 等。随着我们发现 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();
    }
}

即使这样,他们也需要从我的抽象工厂派生出一个工厂来满足他们的特定上下文。

我专门在 html 上下文中构建 foos,如下所示:

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 中。我希望能够从消费者那里传递一个值列表。对于其他 FooFormat,我想从消费者那里传递正确的构造函数参数。但我想让公共 API 保持简单 - 我不希望 BuildFoo() 上出现一堆重载。

那么如何将自定义值列表从 HtmlFooConsumer.DoSomeFoo() 内部传递给 factory.BuildFoo() 调用?任何想法,stackoverflow 大师?

【问题讨论】:

  • 在如此复杂的问题中,有一个更真实的例子真的很有帮助 - 而不是一堆傻瓜。

标签: c# design-patterns dependency-injection factory


【解决方案1】:

也许您可以按照这些思路做一些事情,将您的抽象 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());
    }
}

【讨论】:

  • 太棒了,安德鲁。我试过了,我喜欢这个结果。我牺牲了公共 API 中的一些可发现性,因为消费者必须提前知道 TArg 是什么。但我认为这是一个很好的灵活性折衷。
【解决方案2】:

工厂基本上是静态变量的面向对象版本。我会避免一起使用一个。与其强迫客户使用工厂,或许您可以简单地将对象注入到他们的构造函数中,从而避开对工厂的需求。

【讨论】:

  • 我不确定您所说的“静态变量的面向对象版本”是什么意思。我强制工厂使用以确保客户不能直接构建 Foos,而是让 IFoos 按照他们应该构建的方式构建。依赖 IFoo 而不是 Foo 会强制依赖倒置。
猜你喜欢
  • 2015-05-17
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多