我有一个要实例化的类MyClass
,它包含一个私有属性ObjectRepository
,当调用MyClass
的构造函数时,我想实例化该私有属性。
public MyClass
{
private ObjectRepository _objectRepository;
public MyClass()
{
}
}
MyClass
实例化为要添加到列表中:
objects.Add(new MyClass());
遵循SOLID原则最干净的方法是什么?
----编辑----
我也忘记提及的是,当我为MyClass
编写单元测试时,我希望能够模拟ObjectRepository
。从ObjectRepository
的构造函数实例化MyClass
时是不可能的,因为测试中没有对_objectRepository
的引用。
答案 0 :(得分:4)
为此,您需要查看SOLID中的D:依赖倒置。
MyClass
需要一个ObjectRepository
的实例才能完成其工作;它在ObjectRepository
上具有依赖性。根据依赖倒置原则,它应改为依赖抽象而不是具体类型。在C#中,执行此操作的通常方法是使用接口,例如IObjectRepository
,上面有MyClass
所需的所有方法,但仅此而已。也可以使用抽象基类来完成,但这并不常见。
当然,如果您依赖于接口,则不知道实现该接口的具体类型,因此无法实例化它。这可以;您只需要提供一个实例即可。
public MyClass
{
private IObjectRepository _objectRepository;
public MyClass(IObjectRepository objectRepository)
{
_objectRepository = objectRepository;
}
}
现在,当您创建MyClass
的实例时,必须为它提供实现IObjectRepository
的内容。
objects.Add(new MyClass(new ObjectRepository()));
在许多情况下,这就足够了。但是,对于大型对象图,像这样手动创建所有内容可能既费力又重复。解决此问题的常用方法是使用类似IoC容器的依赖项注入服务。 IoC代表控制反转,因为您正在颠倒事物的创造。 IoC容器不是实例化顶部的内容并让每件事创建所需的东西,而是从底部开始,创建所需的东西,然后使用该容器创建依赖于它的东西,依此类推。可能的用法示例如下:
var container = new SomeIoCContainer();
container.For<IObjectRepository>().Use<ObjectRepository>();
var myClass = container.Resolve<MyClass>();
(示例语法;这从一个IoC容器到另一个IoC容器有所不同,但是它们通常是这样的。)
在许多情况下,您会发现IoC容器可以集成到您的应用程序中,这意味着您甚至不需要显式调用容器来解决问题。例如,ASP.NET是为此目的而构建的,因此,在配置容器之后,您只需将构造函数参数添加到控制器中,框架就会使用该容器为您提供这些参数。 这种方法的优点:
MyClass
内部发生的事情,只需给它提供一个伪造的IObjectRepository
版本,它不会做任何复杂的事情,因此您可以使用工作量大大减少,并且您知道在MyClass
上进行的测试不会受到ObjectRepository
内部可能发生的无关事件的影响。您可以通过为测试设置一个单独的容器来做到这一点,但是通常手动进行起来更容易-您只测试一件事,并且您的模拟依赖项不应依赖任何东西。DatabaseObjectRepository
可以从SQL数据库获取对象,并且想要将其交换为WebClientObjectRepository
来从Web服务获取对象,则只需实现相同的接口并更改您告诉容器IObjectRepository
使用什么的那一行,它将为您交换应用程序中的所有用法。
答案 1 :(得分:2)
从构造函数初始化?
public MyClass
{
private ObjectRepository _objectRepository;
public MyClass()
{
_objectRepository = new ObjectRepository();
}
}
答案 2 :(得分:0)
这应该很简单。 您将在类的构造函数中初始化私有实例,并通过类构造函数传递私有实例所需的所有参数,如下所示:
public MyClass
{
private ObjectRepository _objectRepository;
public MyClass(string connString)
{
// if your ObjectRepository for example requires a connection string param
_objectRepository = new ObjectRepository(connString);
}
}
答案 3 :(得分:0)
如果构造函数不接受参数,并且将为_objectRepository
分配一个不依赖于进行调用的上下文的值,例如没有要执行的验证,则只需:
public MyClass()
{
_objectRepository = new ObjectRepository();
}
编辑:基于OP在其编辑中提供的新信息,我发现Anaximander的答案是最好的答案。