【问题标题】:Create a list of custom objects with Inversion of Control paradigm使用控制反转范式创建自定义对象列表
【发布时间】:2017-01-15 16:56:59
【问题描述】:

我想将依赖注入和控制反转应用到我的日常开发中。假设我有一个SomeObject 的对象类型(实现接口ISomeObject)。我有一个使用这个对象的类,称为 Data ,它实现了IData 接口。

public interface ISomeObject {
    int ID;
    string Name;
    bool IsAwesome;

    void DoSomeStuffIfAwesome();
}

public Class SomeObject : ISomeObject {
    int ID;
    string Name;
    bool IsAwesome;

    void DoSomeStuffIfAwesome() { /*stuff happens here*/ }        
}

public interface IData {
    List<ISomeObject> GetSomeObjects();
}

public Class Data : IData {
    List<ISomeObject> GetSomeObjects()
    {
        List<ISomeObject> objects = new List<ISomeObject>; // ??? Maybe and cast later?

        //do some SQL stuff and get a SqlDataReader object called reader
        while(reader.Read()) {
             //ISomeObject someObj = ???
             //Read into the someObj.ID, someObj.Name and someObj.IsAwesome fields
             objects.add(someObj);
        }
        return objects;
    }
}    

GetSomeObjects() 方法生成ISomeObject 对象的列表。但我不希望Data.cs 将与SomeObject 相关的任何内容硬编码到其中。我想要某种形式的依赖注入来解决运行时的问题。处理这个问题的最佳方法是什么?我考虑了以下几点:

1.将SomeObject 的实例传递给Data 的构造函数。 这样我可以使用.GetType() 获取它的类型,将其存储到Data.cs 中的私有System.Type 变量中,然后在中使用Activator.CreateInstance循环创建要添加到列表中的新对象。 Data 需要了解 SomeObject 类,如果我理解正确的话。

2。将我的 IoC 容器实例传递给 Data 的构造函数,然后使用 container.Resolve&lt;ISomeObject&gt;() 解析对象类型。如果不使用我的 IoC 容器,这将使GetSomeObjects() 方法的单元测试变得困难。我读过我不应该在单元测试期间使用 IoC 容器,而应该手动将我需要的东西传递给方法。

3.传递一个已实例化为 SomeObjectISomeObject 对象 - 然后我将使用它通过一些内置方法创建对象,例如 SomeObject.GenerateList(IDataReader reader)

【问题讨论】:

  • 在某些时候,一个类必须是具体的。为什么IData那个 实现不知道如何返回一组特定的ISomeObject 对象?
  • 很可能就是这种情况。我的理解是 IoC 和依赖注入的目标是让消费者只知道接口,而不是具体类,这样我就可以快速更换具体类,而无需修改消费者。因此,如果将来我想用另一个实现 ISomeObject 的类来替换 SomeObject,我可以在不接触 Data.cs 的情况下这样做。很可能是我对抽象的想法太深入了,需要稍微调整一下。也许我确实需要在 Data.cs 中实例化 SomeObject
  • 使用工厂 (ISomeObjectFactory) 获取阅读器并返回 ISomeObject。将工厂注入Data,工厂将知道如何构造ISomeObject

标签: c# unit-testing dependency-injection inversion-of-control abstraction


【解决方案1】:

您可以将对象的创建委托给其他人;

public interface ISomeObjectFactory {
    ISomeObject Create(IDataReader reader);
}

它只负责创建ISomeObject 的实例

using System.Collections.Generic;
using System.Data;

public interface IDbConnectionFactory {
    ///<summary>
    ///  Creates a connection based on the given database name or connection string.
    ///</summary>
    IDbConnection CreateConnection(string nameOrConnectionString);
}

public class Data : IData {
    private IDbConnectionFactory dbConnectionFactory;
    ISomeObjectFactory someObjectFactory;
    private string CONNECTION_STRING = "Connection string here";

    public Data(IDbConnectionFactory dbConnectionFactory, ISomeObjectFactory objectFactory) {
        this.dbConnectionFactory = dbConnectionFactory;
        this.someObjectFactory = objectFactory;
    }

    public List<ISomeObject> GetSomeObjects() {
        var objects = new List<ISomeObject>();
        //do some SQL stuff and return a data reader
        using (var connnection = dbConnectionFactory.CreateConnection(CONNECTION_STRING)) {
            using (var command = connnection.CreateCommand()) {
                //configure command to be executed.
                command.CommandText = "SELECT * FROM SOMEOBJECT_TABLE";
                connnection.Open();
                using (var reader = command.ExecuteReader()) {
                    while (reader.Read()) {
                        //...Logic to populate item
                        var someObject = someObjectFactory.Create(reader);
                        if (someObject != null)
                            objects.Add(someObject);
                    }
                }
            }
        }

        return objects;
    }
}

那样Data 只依赖于抽象而不依赖于具体化。这些可以在运行时在组合根中确定/配置。

【讨论】:

  • 这个答案很棒,应该可以解决问题!非常感谢=D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-14
  • 2021-12-19
  • 1970-01-01
  • 2015-11-16
  • 1970-01-01
相关资源
最近更新 更多