【问题标题】:C# Dependency Container and constructorsC# 依赖容器和构造函数
【发布时间】:2016-03-31 14:17:33
【问题描述】:

我花了一些时间记录自己关于依赖注入和 IoC 的内容,但我还没有找到解决问题的方法。

我的问题与使用依赖容器时对象的实例化有关,因为它创建了对构造函数参数的依赖。在我遇到的几乎每个示例中,具体类的构造函数都没有任何参数。它使一切都变得“简单”。因此我的问题在这里。

举个例子:我需要从两个源A和B下载一些数据。源A包含各种格式的数据;例如 csv 和 xml。我们不需要为源 B 指定这样的东西。

这是一些代码(请注意,我尽可能简化了代码以说明我的观点):

using System.Net;
using System.IO;
using System.Reflection;

namespace Question
{
  class Program
  {
    static void Main(string[] args)
    {
        //exemple of code using Client A
        DependencyContainer container1 = GetContainer1();
        IClient client1 = container1.Resolve<IClient>("xml");
        User user1 = new User(client1);
        user1.run();

        DependencyContainer container2 = GetContainer2();
        IClient client2 = container2.Resolve<IClient>();
        User user2 = new User(client2);
        user2.run();
    }

    public static DependencyContainer GetContainer1()
    {
        DependencyContainer container = new DependencyContainer();
        container.Register<IClient, ClientA>();
        return container;
    }

    public static DependencyContainer GetContainer2()
    {
        DependencyContainer container = new DependencyContainer();
        container.Register<IClient, ClientB>();
        return container;
    }
}

public class User
{
    private readonly IClient _Client;

    public User(IClient client)
    {
        _Client = client;
    }

    public void run()
    {
        string address = _Client.getAddress();
        string data = _Client.getData(address);
        _Client.writeData(data);
    }
}
// Abstraction
public interface IClient
{
    /// <summary>
    /// create the address or the name of the file storing the data
    /// </summary>
    string getAddress();

    /// <summary>
    /// uses a WebClient to go and get the data at the address indicated
    /// </summary>
    string getData(string adress);

    /// <summary>
    /// Write the data in a local folder
    /// </summary>
    void writeData(string data);
}

//Implementation A
public class ClientA : IClient
{
    // Specify the type of the file to be queried in the database
    // could be a csv or an xml for example
    private readonly string _FileType;

    public ClientA(string fileType)
    {
        _FileType = fileType;
    }

    public string getAddress()
    {
        return "addressOfFileContainingData." + _FileType;
    }

    public string getData(string address)
    {
        string data = string.Empty;
        using (WebClient client = new WebClient())
        {
            data = client.DownloadString(address);
        }
        return data;
    }

    public void writeData(string data)
    {
        string localAddress = "C:/Temp/";
        using (StreamWriter writer = new StreamWriter(localAddress))
        {
            writer.Write(data);
        }
    }
}

//Implementation B
public class ClientB : IClient
{
    public ClientB()
    {
    }

    public string getAddress()
    {
        return "addressOfFileContainingData";
    }

    public string getData(string address)
    {
        string data = string.Empty;
        using (WebClient client = new WebClient())
        {
            data = client.DownloadString(address);
        }
        return data;
    }

    public void writeData(string data)
    {
        string localAddress = "C:/Temp/";
        using (StreamWriter writer = new StreamWriter(localAddress))
        {
            writer.Write(data);
        }
    }
}

public class DependencyContainer
{
    private Dictionary<Type, Type> _Map = new Dictionary<Type, Type>();

    public void Register<TypeToResolve, ResolvedType>()
    {
        _Map.Add(typeof(TypeToResolve), typeof(ResolvedType));
    }

    public T Resolve<T>(params object[] constructorParameters)
    {
        return (T)Resolve(typeof(T), constructorParameters);
    }

    public object Resolve(Type typeToResolve, params object[] constructorParameters)
    {
        Type resolvedType = _Map[typeToResolve];
        ConstructorInfo ctorInfo = resolvedType.GetConstructors().First();
        object retObject = ctorInfo.Invoke(constructorParameters);
        return retObject;
    }
}

}

我倾向于认为这段代码有一些优点,但请随时纠正我。但是,实例化:

IClient client = container.Resolve<IClient>("xml");

IClient client = container.Resolve<IClient>();

引起了我很多关注。高级模块(此处为 User 类)不依赖于预期的具体实现。但是,现在类 Program 依赖于具体类的构造函数的结构!因此,它通过在其他地方制造更大的问题来解决一个问题。我宁愿依赖于具体的实现,而不是依赖于其构造函数的结构。假设 ClientA 的代码被重构,构造函数被改变,那么我不知道类 Program 实际使用它。

最后,我的问题:

  1. 我错过了 IoC 的重点吗?
  2. 我错过了使用它吗?
  3. 如果不是,如何解决这个问题?

一种解决方案是在 ClientA 的构造函数中不包含任何参数。但这是否意味着构造函数在使用依赖容器时不应该有任何参数?或者这是否意味着在其构造函数中带有参数的对象不适合这种技术? 也有人会争辩说,ClientA 和 ClientB 不应该派生自同一个接口,因为它们本质上的行为方式不同。

感谢您的 cmets 和投入。

【问题讨论】:

  • 在发布上述消息后,以下帖子被标记为相关:stackoverflow.com/questions/8169657/…。实际上,参数可以移动到实际使用它的方法中。但是它会破坏接口(这就是我所说的他们不应该实现相同的单一接口的意思)。如果这是对我所举的具体示例的实际答案,我的主要观点仍然是:使用 IoC 可以将对类型的依赖转变为对其构造函数结构(其参数)的依赖
  • 为什么不在你的容器周围使用工厂。Resolve() 方法调用?您实现的某些地方必须确定您要使用的真实类,所以工厂对我来说很有意义。如果您向构造函数注入或多或少的静态实现,IoC 本身就非常好——从一开始就在容器配置中确定的实现,而不是您在代码中手动创建的实现。否则,您必须按照在工厂或其他类似对象中封装的方式进行。

标签: c# constructor dependency-injection ioc-container


【解决方案1】:

我错过了 IoC 的重点吗?

是和不是。幸运的是,您的具体类(UserClientAClientB)都依赖于 构造函数注入,这是最重要的依赖注入 (DI) 模式。另一方面,DI 容器完全是可选的。

因此,使用Pure DI,您只需像这样实现您的Main 方法:

static void Main(string[] args)
{
    //exemple of code using Client A
    User user1 =
       new User(
           new ClientA(
               "xml"));
    user1.run();

    User user2 =
        new User(
            new ClientB());
    user2.run();
}

这不仅让每个人都容易理解,而且在您编写对象图时还会给您compile-time feedback

DI 最重要的目标是确保实现代码适当解耦,这正是构造函数注入的帮助。

我错过了使用它吗?

也许有一点,但不多。如果您希望使用 DI 容器而不是 Pure DI,您应该遵循Register Resolve Release pattern。如果你想要一个 User 对象,你应该请求它,而不是请求一个 IClient 对象:

var user = container.Resolve<User>();
user.run();

您可以在容器中适当地注册所有服务。如果你想使用ClientA,你需要告诉容器它应该为fileType构造函数参数使用哪个值。具体如何执行取决于您使用的特定 DI 容器。

不过,有时您可以为primitive dependencies 定义一个约定,例如pulling all primitive values from the application's configuration file

如果不是,如何解决这个问题?

我的建议是使用上述纯 DI 方法,除非您有 compelling reason to use a DI Container。根据我的经验,这种情况很少发生。

【讨论】:

    猜你喜欢
    • 2011-12-29
    • 1970-01-01
    • 2019-03-24
    • 1970-01-01
    • 2012-03-19
    • 2011-02-02
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    相关资源
    最近更新 更多