C#依赖注入的副作用(两步初始化反模式)? [英] C# Dependency injection side effect (two step initialization anti-pattern)?

查看:176
本文介绍了C#依赖注入的副作用(两步初始化反模式)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我工作的一个项目,在我的构造函数包含 - 只 - 行为的依赖。 ,即每当我路过值/状态



例如:

 类ProductProcessor:IProductProcessor 
{
公共双的someMethod(){...}
}
类PackageProcessor
{
私人只读IProductProcessor _productProcessor;
私人双_taxRate;

公共PackageProcessor(IProductProcessor productProcessor)
{
_productProcessor = productProcessor;
}

公共初始化(双TAXRATE)
{
_taxRate = TAXRATE;
返回这一点;
}

公共双ProcessPackage()
{
返回_taxRate * _productProcessor.SomeMethod();
}

}

为了传递状态时,会议决定列入第二步(调用初始化)。



我知道我们可以在这个配置作为IOC容器配置类命名参数,但是,我们做不喜欢创造新namedParameter(paramvalue)的想法在配置文件中的,因为它使得它不必要的读取,并创建一个未来的维护痛点。



我。已经看到在多个地方这种模式



问:我看了一些认为这两步初始化一个反模式。如果是这样的共识,这是不是意味着通过IoC容器依赖注入的方式限制/各种各样的弱点是什么?



修改
寻找到马克·西曼的的建议



和答案这一次,我有几点看法:
初始化/应用:同意它作为一个反面模式/气味。
雅各布·马萨德:我同意IoC容器中,都出现了问题,当涉及到原始的依赖关系。手册(穷人的)DI,描述这里听起来不错较小或建筑。稳定的系统,但我认为它可能成为很难维持一些手动配置构成根



选项:
1)工厂的依赖(运行时的时间分辨率是必需的)
2)从纯业务的隔离状态的对象所描述这里



(1):这是我一直在做什么,但我意识到,有承担成另一种抗模式的潜力:服务定位器。
(2):我对我的具体情况的偏好是这个关于这个我可以清晰地分离这两种类型。纯粹的服务是没有道理的 - IoC容器,而状态的对象分辨率将取决于他们是否有原始的依赖与否



每当我不得不使用依赖注入,它已被教条的方式使用,一般在主管执意不惜任何代价与IoC容器应用DI的订单。


解决方案

我读了一些认为这两步初始化一个反模式




初始化方法导致时间耦合。称这是 可能过于严格,但它肯定是一个的设计嗅觉



如何提供这个值该组件依赖于它是什么类型的值。有两种口味:配置值和运行值:




  • 配置值:如果是组件的生命周期中不会改变不变/配置值,该值应直接注入的构造。


  • 运行值:如果运行期间的数值变化(如要求特定的值),该值应的的初始化期间提供(无论通过构造也使用一些初始化方法)。初始化与运行时数据的组件实际上是一个反模式




我部分带有@YacoubMassad同意有关使用DI容器原始依赖的配置。由容器提供的API不支持使用自动布线时可维护的方式设置这些值。我认为这主要是由于在C#和.NET的限制。我挣扎了很长一段时间这样的API,而设计和开发简单的注射器,但决定彻底离开了这样的API,因为我没T找到一种方法来定义,这是既直观的API,并导致代码是用户容易维护。正因为如此,我通常建议开发人员注册需要使用lambda语法(原语在类型注册< IProductProcessor>(()=>新建ProductProcessor(TAXRATE:3))),而不是使用容器的自动装配设备(其中,容器试图在所有的依赖,以填补你)。



但正如我所说,我只部分同意与雅各布。虽然它是比较实用的手(也称为纯DI)撰写你的一些对象,他暗示,这意味着你应该完全放弃DI容器。国际海事组织是太强烈放。在大多数我写的应用程序,我批注册使用容器类型我98%左右,而我的手线的另外两个2%,因为自动布线它们太复杂。这在我的应用程序最佳的整体结果文本中提供的。当然,你可能会有所不同。并不是每一个应用程序真正从使用DI容器的好处,我不会在所有我写的应用程序中使用的容器自己。但我一直做的不过是应用依赖注入模式和SOLID原则。


I'm working on a project in which my constructors contain - only - behavioral dependencies. i.e. I never pass values / state.

Example:

class ProductProcessor : IProductProcessor
{
   public double SomeMethod(){ ... }
}
class PackageProcessor
{
   private readonly IProductProcessor _productProcessor;
   private double _taxRate;

   public PackageProcessor(IProductProcessor productProcessor)
   {
        _productProcessor = productProcessor;
   }

   public Initialize(double taxRate)
   {
       _taxRate = taxRate;
       return this;
   }

   public double ProcessPackage()
   {
       return _taxRate * _productProcessor.SomeMethod();
   }

}

In order to pass state, it was decided to include a second step (a call to Initialize).

I know we can configure this as a named parameter in the IoC Container config class, however, we did not like the idea of creating "new namedParameter(paramvalue)'s" in the configuration file as it makes it unnecessarily unreadable and creates a future maintenance pain spot.

I've seen this pattern in more than one place.

Question: I read some consider this two step initialization an anti-pattern. If that is the consensus, wouldn't this imply a limitation / weakness of sorts in the approach of dependency injection via a IoC container?

Edit: After looking into Mark Seeman's suggestion:

and the answers to this one, I have a few comments: Initialize/Apply : Agree on it being an anti pattern / smell. Yacoub Massad: I agree IoC containers are a problem when it comes to primitive dependencies. Manual (poor man's) DI, as described here sounds great for smaller or architecturally stable systems but I think it could become very hard to maintain a number of manually configured composition roots.

Options: 1)Factories as dependencies (when run time resolution is required) 2) Separate stateful object from pure services as described here.

(1): This is what I had been doing but I realized that there is a potential to incur into a another anti-pattern: the service locator. (2): My preference for my particular case is this one about this one as I can cleanly separate both types. Pure services are a no brainer - IoC Container, whereas stateful object resolution will depend on whether they have primitive dependencies or not.

Every time I've 'had' to use dependency injection, it has been used in a dogmatic way, generally under the orders of a supervisor bent on applying DI with IoC container at any cost.

解决方案

I read some consider this two step initialization an anti-pattern

The Initialize method leads to Temporal Coupling. Calling it an anti-pattern might be too strict, but it sure is a Design Smell.

How to provide this value to the component depends on what type of value it is. There are two flavors: configuration values and runtime values:

  • Configuration Values: If it is a constant/configuration value that won't change during the lifetime of the component, the value should be injected into the constructor directly.

  • Runtime values: In case the value changes during runtime (such as request specific values), the value should not be provided during initialization (neither through the constructor nor using some Initialize method). Initializing components with runtime data actually IS an anti-pattern.

I partly agree with @YacoubMassad about the configuration of primitive dependencies using DI containers. The APIs provided by containers do not enable setting those values in a maintainable way when using auto-wiring. I think this is mainly caused by limitations in C# and .NET. I struggled a long time with such API while designing and developing Simple Injector, but decided to leave out such API completely, because I didn't find a way to define an API that was both intuitive and lead to code that was easy maintainable for the user. Because of this I usually advise developers to register types that require primitives using the lambda syntax (as in Register<IProductProcessor>(() => new ProductProcessor(taxRate: 3))) as opposed to using the container's auto-wiring facilities (where the container tries to fill in all dependencies for you).

But as I said, I only partly agree with Yacoub. Although it is more practical to compose some of your objects by hand (a.k.a. Pure DI), he implies that this means you should abandon DI containers completely. IMO that is too strongly put. In most of the applications I write, I batch-register about 98% of my types using the container, and I hand-wire the other two 2%, because auto-wiring them is too complex. This gives in the context of my applications the best overall result. Of course, you're mileage may vary. Not every application really benefits from using a DI container, and I don't use a container myself in all the application I write. But what I always do however, is apply the Dependency Injection pattern and the SOLID principles.

这篇关于C#依赖注入的副作用(两步初始化反模式)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆