如何为对象工厂提供不同的参数?

时间:2018-02-13 11:38:28

标签: c# .net design-patterns factory

我有一个包含一些实现相同界面的类的库:

internal class MyObj1 : IMyObj {
     public MyObj1(string param1, int param2) {}
}

internal class MyObj2 : IMyObj {
     public MyObj2(bool param1, string param2, int param3) {}
}

internal class MyObj3 : IMyObj {
     public MyObj3(string param1, int param2) {}
}

我想创建一个对象工厂,只允许通过IMyObj访问MyObj1,MyObj2,MyObj3:

public class MyObjFactory {
    public IMyObj Create<T>() {
        return (IMyObj)Activator.CreateInstance(typeof(T));
    }
}

我不知道如何将构造函数参数传递给factory方法。有什么想法吗?

3 个答案:

答案 0 :(得分:1)

使用Activator.CreateInstance Method (Type, Object[])

  

使用构造函数创建指定类型的实例   最符合指定的参数。

public IMyObj Create<T>(params object[] args) 
{
    return (IMyObj)Activator.CreateInstance(typeof(T),args);
}

另外

public IMyObj Create<T>(string param1, int param2) where T : MyObj1 
{
    return (IMyObj)Activator.CreateInstance(typeof(T),args);
}

public IMyObj Create<T>(bool param1, string param2, int param3) where T : MyObj2 
{
    return (IMyObj)Activator.CreateInstance(typeof(T),args);
}
...
...

答案 1 :(得分:1)

我建议使用Activator.CreateInstance因为它相对较慢,并且运行时安全性有所降低(例如,如果你得到错误的构造函数参数,它会引发异常在运行时)。

我会建议像:

public IMyObj CreateType1(string param1, int param2)
{
    return new MyObj1(param1, param2);
}    

public IMyObj CreateType2(bool param1, string param2, int param3) 
{
    return new MyObj2(param1, param2, param3);
}

答案 2 :(得分:1)

听起来这就是你所处的位置:

a)您不希望类创建它们所依赖的其他类,因为它们将它们耦合在一起。每个类都必须了解它所依赖的类,例如它们的构造函数参数。

b)您创建一个工厂来分隔这些对象的创建。

c)你发现你在(a)中遇到的问题现在已转移到(b),但问题完全相同,只有更多的类。现在你的工厂必须创建类实例。但是它会在哪里获得创建这些对象所需的构造函数参数?

一种解决方案是使用DI容器。如果这完全熟悉那么10%的坏消息和90%的好消息。这有点学习曲线,但也不错。 90%的好消息是,你已经达到了你意识到自己需要它的程度,并且它将成为一个非常有价值的工具。

当我说&#34; DI容器&#34; - 也称为&#34; IoC(控制反转)容器,&#34;指的是像Autofac,Unity或Castle Windsor这样的工具。我主要与温莎合作,所以我在例子中使用它。

DI容器是一种为您创建对象而无需显式调用构造函数的工具。 (这种解释100%肯定是不够的 - 你需要谷歌更多。相信我,它是值得的。)

假设你有一个依赖于几个抽象(接口)的类。这些接口的实现依赖于更多的抽象:

public class ClassThatDependsOnThreeThings
{
    private readonly IThingOne _thingOne;
    private readonly IThingTwo _thingTwo;
    private readonly IThingThree _thingThree;

    public ClassThatDependsOnThreeThings(IThingOne thingOne, IThingTwo thingTwo, IThingThree thingThree)
    {
        _thingOne = thingOne;
        _thingTwo = thingTwo;
        _thingThree = thingThree;
    }
}

public class ThingOne : IThingOne
{
    private readonly IThingFour _thingFour;
    private readonly IThingFive _thingFive;

    public ThingOne(IThingFour thingFour, IThingFive thingFive)
    {
        _thingFour = thingFour;
        _thingFive = thingFive;
    }
}

public class ThingTwo : IThingTwo
{
    private readonly IThingThree _thingThree;
    private readonly IThingSix _thingSix;

    public ThingTwo(IThingThree thingThree, IThingSix thingSix)
    {
        _thingThree = thingThree;
        _thingSix = thingSix;
    }
}

public class ThingThree : IThingThree
{
    private readonly string _connectionString;

    public ThingThree(string connectionString)
    {
        _connectionString = connectionString;
    }
}

这很好,因为每个班级都很简单,也很容易测试。但是,你是如何创建一个工厂来为你创建所有这些对象的呢?该工厂必须知道/包含创建每个对象所需的一切。

各个类的状况更好,但是编写它们或创建实例会成为一个主要问题。如果您的代码部分只需要其中一部分,那么您是否创建了另一个工厂?如果您必须更改其中一个类以便现在它具有更多或不同的依赖项,该怎么办?现在你必须回去修理你所有的工厂。那是一场噩梦。

DI容器(再次,此示例使用Castle.Windsor)允许您执行此操作。起初它看起来更像是工作,或只是解决问题。但事实并非如此:

var container = new WindsorContainer();
container.Register(
    Component.For<ClassThatDependsOnThreeThings>(),
    Component.For<IThingOne, ThingOne>(),
    Component.For<IThingTwo, ThingTwo>(),
    Component.For<IThingThree, ThingThree>()
        .DependsOn(Dependency.OnValue("connectionString", ConfigurationManager.ConnectionStrings["xyz"].ConnectionString)),
    Component.For<IThingFour,IThingFour>(),
    Component.For<IThingFive, IThingFive>(),
    Component.For<IThingSix, IThingSix>()
);

现在,如果你这样做:

var thing = container.Resolve<ClassThatDependsOnThreeThings>();

var thingTwo = container.Resolve<IThingTwo>();

只要您使用容器注册了类型,并且您还注册了满足所有嵌套依赖项所需的任何类型,容器会根据需要创建每个对象,调用每个对象的构造函数,直到它最终可以创建你要求的对象。

您可能会注意到的另一个细节是,这些类中没有一个能够创建它们所依赖的东西。没有new ThingThree()。无论每个类依赖于什么,都在其构造函数中指定。这是依赖注入的基本概念之一。如果一个类只是接收IThingThree的实例,那么它实际上永远不知道实现是什么。它只取决于界面,并且对实现一无所知。这适用于依赖倒置,&#34; D&#34;在SOLID中。它有助于保护您的类不与特定的实现细节相结合。

非常强大。这意味着,在正确配置后,在代码中的任何位置,您都可以询问所需的依赖关系 - 通常作为接口 - 并且只是接收它。需要它的课程不必知道如何创建它。这意味着90%的时间你根本不需要工厂。你的类的构造函数只是说它需要什么,容器提供它。

(如果你确实需要一个工厂,在某些情况下会发生这种情况,Windsor和其他一些容器可以帮助你创建一个工厂。Here's an example

让这个工作的一部分涉及学习如何配置您使用DI容器的应用程序类型。例如,在ASP.NET MVC应用程序中,您将配置容器以为您创建控制器。这样,如果您的控制器依赖于更多东西,容器可以根据需要创建这些东西。 ASP.NET Core通过提供自己的DI容器使其变得更容易,因此您只需注册各种组件即可。

这是一个不完整的答案,因为它描述了解决方案的内容,而没有告诉您如何实现它。这将需要您进行更多搜索,例如&#34;如何配置XYZ以进行依赖注入,&#34;或者只是更多地了解这个概念。一位作者将其称为$ .50概念的$ 5术语。它看起来很复杂,令人困惑,直到你尝试它并看看它是如何工作的。然后,您将了解为什么它内置于ASP.NET Core,Angular以及为什么各种语言都使用依赖注入。

当你达到目的 - 就像你所遇到的那样 - 你解决了DI所解决的问题时,这真的令人兴奋,因为这意味着你意识到必须有一种更好,更清洁的方式来实现你所做的事情。重新尝试。好消息是有。学习和使用它会在整个代码中产生连锁反应,使您能够更好地应用SOLID原则并编写更易于单元测试的较小类。