用需要实例化的私有对象实例化类的最佳方法

时间:2019-05-14 08:31:08

标签: c# solid-principles

我有一个要实例化的类MyClass,它包含一个私有属性ObjectRepository,当调用MyClass的构造函数时,我想实例化该私有属性。

public MyClass
{
    private ObjectRepository _objectRepository;

    public MyClass()
    {

    }
}

MyClass实例化为要添加到列表中:

objects.Add(new MyClass());

遵循SOLID原则最干净的方法是什么?

----编辑----

我也忘记提及的是,当我为MyClass编写单元测试时,我希望能够模拟ObjectRepository。从ObjectRepository的构造函数实例化MyClass时是不可能的,因为测试中没有对_objectRepository的引用。

4 个答案:

答案 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内部可能发生的无关事件的影响。您可以通过为测试设置一个单独的容器来做到这一点,但是通常手动进行起来更容易-您只测试一件事,并且您的模拟依赖项不应依赖任何东西。
  • 随着应用程序的增长和变得越来越复杂,您不必担心如何创建事物所依赖的所有组件-您只需告诉IoC容器它们是什么,它将负责创建事物。
  • 如果您需要更改其中一个依赖项的工作方式,这会容易得多。例如,如果您有一个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的答案是最好的答案。