我有一个包含一些实现相同界面的类的库:
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方法。有什么想法吗?
答案 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原则并编写更易于单元测试的较小类。