我发现我的构造函数越来越长了:
public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )
由于参数列表不断增加。因为“Container”是我的依赖注入容器,我为什么不能这样做:
public MyClass(Container con)
每个类都要使用控制反转(IoC)和依赖注入(DI)吗?这样做有什么缺点吗?如果我这样做,感觉就像在使用一个过于复杂的静态工具。请分享您对IoC和DI热潮的想法。
我发现我的构造函数越来越长了:
public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )
由于参数列表不断增加。因为“Container”是我的依赖注入容器,我为什么不能这样做:
public MyClass(Container con)
每个类都要使用控制反转(IoC)和依赖注入(DI)吗?这样做有什么缺点吗?如果我这样做,感觉就像在使用一个过于复杂的静态工具。请分享您对IoC和DI热潮的想法。
您说得对,如果将容器用作服务定位器,它基本上就是一个不折不扣的静态工厂。出于许多原因,我认为这是一种反模式(请参见我书中的此处摘录)。
构造函数注入的一个美妙好处是,它使单一职责原则的违反非常明显。
当出现这种情况时,就是进行外观服务重构的时候了。简而言之,创建一个新的、更粗粒度的接口,隐藏当前所需的某些或所有细粒度依赖项之间的交互。
我认为您的类构造函数不应该直接引用IOC容器。这样会导致类与容器之间存在不必要的依赖关系(而IOC正是试图避免这种类型的依赖关系!)。
MyClass myClass = new MyClass(IDependency1 interface1, IDependency2 interface2)
(接口参数)。这与@derivation的帖子无关,我理解为依赖注入容器不应将自身注入其对象中,即MyClass myClass = new MyClass(this)
。 - John Doeclass MyTypeFactory
{
private readonly IServiceProvier mServices;
public MyTypeFactory(IServiceProvier services) => mServices = services;
MyType Create(int param) => ActivatorUtilities.CreateInstance(mServices, param);
}
- MikeActivatorUtilities
是您可以避免传递容器的方法。请参见此处的答案:https://dev59.com/OHUOtIcB2Jgan1znv2u6#72482813 - Shai Cohen传递参数的难度不是问题,问题在于你的类功能过于多,需要进一步拆分。
依赖注入可以作为一个早期警报,用于防止类变得过于庞大,特别是因为要传递所有依赖项而导致的越来越大的麻烦。
问题:
1) 构造函数的参数列表逐渐增加。
2) 如果类被继承(例如:RepositoryBase
),则更改构造函数签名会导致派生类的变化。
解决方案 1
将IoC容器
传递给构造函数
为什么
为什么不
解决方案 2
创建一个类,将所有服务分组并将其传递给构造函数
public abstract class EFRepositoryBase
{
public class Dependency
{
public DbContext DbContext { get; }
public IAuditFactory AuditFactory { get; }
public Dependency(
DbContext dbContext,
IAuditFactory auditFactory)
{
DbContext = dbContext;
AuditFactory = auditFactory;
}
}
protected readonly DbContext DbContext;
protected readonly IJobariaAuditFactory auditFactory;
protected EFRepositoryBase(Dependency dependency)
{
DbContext = dependency.DbContext;
auditFactory= dependency.JobariaAuditFactory;
}
}
派生类
public class ApplicationEfRepository : EFRepositoryBase
{
public new class Dependency : EFRepositoryBase.Dependency
{
public IConcreteDependency ConcreteDependency { get; }
public Dependency(
DbContext dbContext,
IAuditFactory auditFactory,
IConcreteDependency concreteDependency)
{
DbContext = dbContext;
AuditFactory = auditFactory;
ConcreteDependency = concreteDependency;
}
}
IConcreteDependency _concreteDependency;
public ApplicationEfRepository(
Dependency dependency)
: base(dependency)
{
_concreteDependency = dependency.ConcreteDependency;
}
}
为什么
为什么不
解决方案2只是一个初步的想法,如果有充分的反对意见,则欢迎提供描述性评论。
我已经仔细阅读了这个帖子两次,我认为人们的回应是基于他们所知道的而不是所问的。
JP最初的问题似乎是通过发送解析器和一堆类来构建对象,但我们假设这些类/对象本身就是服务,可以进行注入。那么如果它们不是呢?
JP,如果你想利用DI并且想要将注入与上下文数据混合使用,那么这些模式(或所谓的“反模式”)都没有特别涉及到。实际上,这归结为使用一个支持此类尝试的软件包。
Container.GetSevice<MyClass>(someObject1, someObject2)
……很少有支持这种格式的。我认为编写此类支持程序的困难程度,加上与实现相关的可怜性能,使其对开源开发人员不具吸引力。
但是应该这样做,因为我应该能够创建并注册MyClass的工厂,并且该工厂应该能够接收不必为传递数据/模型而存在的“服务”的数据/输入。如果“反模式”是指负面后果,则强制存在用于传递数据/模型的人造服务类型肯定是负面的(与将类包装到容器中的感觉相同。同样的本能也适用)。
尽管它们看起来有点丑陋,但有些框架可能会有所帮助。例如,Ninject:
这是针对.NET的,很受欢迎,但仍然不如应该,但我敢肯定无论您选择使用哪种语言,都会有一些东西可以帮助你。
这是我使用的方法
public class Hero
{
[Inject]
private IInventory Inventory { get; set; }
[Inject]
private IArmour Armour { get; set; }
[Inject]
protected IWeapon Weapon { get; set; }
[Inject]
private IAction Jump { get; set; }
[Inject]
private IInstanceProvider InstanceProvider { get; set; }
}
以下是一种简单的方法,用于执行注入并在注入值后运行构造函数。 这是一个完全功能的程序。
public class InjectAttribute : Attribute
{
}
public class TestClass
{
[Inject]
private SomeDependency sd { get; set; }
public TestClass()
{
Console.WriteLine("ctor");
Console.WriteLine(sd);
}
}
public class SomeDependency
{
}
class Program
{
static void Main(string[] args)
{
object tc = FormatterServices.GetUninitializedObject(typeof(TestClass));
// Get all properties with inject tag
List<PropertyInfo> pi = typeof(TestClass)
.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(info => info.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0).ToList();
// We now happen to know there's only one dependency so we take a shortcut just for the sake of this example and just set value to it without inspecting it
pi[0].SetValue(tc, new SomeDependency(), null);
// Find the right constructor and Invoke it.
ConstructorInfo ci = typeof(TestClass).GetConstructors()[0];
ci.Invoke(tc, null);
}
}
我目前正在进行一项业余项目,它的工作方式如下https://github.com/Jokine/ToolProject/tree/Core
一个方法有太多参数可能是一个提示(不一定)你的方法太大/责任过多。
此外,在同一个方法中使用的参数(可能)具有高内聚性 -> 值得考虑将它们放入一个类中 -> 只需要传递一个参数。