标签:c dependency-injection constructor ioc-container
我花了一些时间记录自己的依赖注入和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类)不依赖于预期的具体实现.但是,现在类程序依赖于具体类的构造函数的结构!因此,它通过在其他地方创建更大的问题来解决一个问题.我宁愿依赖于具体的实现而不是它的构造函数的结构.让我们假设ClientA的代码被重构并且构造函数被更改,那么我不知道类Program实际上是否使用它.
最后,我的问题:
>我错过了IoC的观点吗?
>我想念它吗?
>如果没有,如何解决这个问题?
一种解决方案是不要在ClientA的构造函数中有任何参数.但这是否意味着构造函数在使用依赖容器时应该永远不会有任何参数?或者这是否意味着在其构造函数中具有参数的对象不适合这种技术?
人们还可以争辩说ClientA和ClientB不应该从相同的单一接口派生,因为它们本质上不会以相同的方式运行.
感谢您的意见和建议.
解决方法:
have I missed the point of IoC?
是的,不是.幸运的是,您的具体类(User,ClientA,ClientB)都依赖于Constructor Injection,这是最重要的依赖注入(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最重要的目标是确保实现代码适当地解耦,这是Constructor Injection帮助做的事情.
Am I miss-using it?
或许,但也不多.如果您希望使用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.
If not, how to solve this issue?
我的建议是使用上面的Pure DI方法,除非你有compelling reason to use a DI Container.根据我的经验,这很少发生.
标签:c,dependency-injection,constructor,ioc-container 来源: https://codeday.me/bug/20190611/1220845.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。