Introduction
介绍
Chapter 1 outlines how you can address some of the most common requirements in enterprise applications by adopting a loosely coupled design to minimize the dependencies between the different parts of your application. However, if a class does not directly instantiate the other objects that it needs, some other class or component must take on this responsibility. In this chapter, you’ll see some alternative patterns that you can use to manage how objects are instantiated in your application before focusing specifically on dependency injection as the mechanism to use in enterprise applications.
第1章概述了如何通过采用松散耦合设计来最小化应用程序的不同部分之间的依赖关系,从而解决企业应用程序中的一些最常见的需求。但是,如果一个类不直接实例化它需要的其他对象,一些其他类或组件必须承担这个责任。在本章中,您将看到一些可用于管理对象在应用程序中如何实例化的替代模式,在你明确你的企业应用使用以来注入机制之前。
Factories, Service Locators, and Dependency Injection
工厂、服务定位和依赖注入
Factories, service locators, and dependency injection are all approaches you can take to move the responsibility for instantiating and managing objects on behalf of other client objects. In this section, you’ll see how you can use them with the same example you saw in the previous chapter. You’ll also see the pros and cons of the different approaches and see why dependency injection can be particularly useful in enterprise applications.
工厂、服务定位和依赖注入是你可以采取的所有途径,用于转移替代其他客户对象实例化和管理对象的职责。在本节中,您将看到如何使用上一章中所示的示例。您还将看到不同方法的优缺点,并了解为什么依赖注入在企业应用程序中特别有用。
Factory Patterns
工厂模式
There are three common factory patterns. The Factory Method and Abstract Factory patterns from “Design Patterns: Elements of Reusable Object-Oriented Software” by Gamma, Erich, Richard Helm,Ralph Johnson, and John Vlissides. Addison Wesley Professional, 1994., and the Simple Factory pattern.
有三种常见的工厂模式。工厂方法和抽象工厂模式来自Gamma, Erich, Richard Helm,Ralph Johnson, and John Vlissides.Addison Wesley Professional, 1994“设计模式:可重用的面向对象软件的元素”,和简单工厂模式。
The Factory Method Pattern
工厂方法模式
The following code samples show how you could apply the factory method pattern to the example shown in the previous chapter. The first code sample shows how you could use a factory method to return an instance of the TenantStore class to use in the ManagementController class. In this example, the CreateTenantStore method is the factory method that creates the TenantStore instance and the Index method uses this instance as part of its logic.
以下代码示例显示如何将工厂方法模式应用于上一章中所示的示例。第一段代码示例演示如何使用工厂方法返回ManagementController 类中使用的TenantStore 类的实例。在这个示例中,CreateTenantStore 方法是一个创建TenantStore 示例的工厂方法,Index方法使用此实例作为其逻辑的一部分。
public class ManagementController : Controller { protected ITenantStore tenantStore; public ManagementController() { this.tenantStore = CreateTenantStore(); } protected virtual ITenantStore CreateTenantStore() { var storageAccount = AppConfiguration.GetStorageAccount("DataConnectionString"); var tenantBlobContainer = new EntitiesContainer<Tenant>(storageAccount, "Tenants"); var logosBlobContainer = new FilesContainer(storageAccount, "Logos", "image/jpeg"); return new TenantStore(tenantBlobContainer, logosBlobContainer); } public ActionResult Index() { var model = new TenantPageViewData<IEnumerable<string>>(this.tenantStore.GetTenantNames()) { Title = "Subscribers" }; return this.View(model); } ... }
Using this approach does not remove the dependencies the Management-Controller has on the TenantStoreclass, nor the FilesContainer and Entities-Container classes. However, it is now possible to replace the underlying storage mechanism without changing the existing ManagementController class as the following code sample shows.
使用这种方法不能删除ManagementController对TenantStore 类的依赖性,也不会删除FilesContainer和EntitiesContainer类。但是,他现在可以替换底层的存储机制而不用更改现有的ManagementController 类,就像下面代码所演示的。
public class SQLManagementController : ManagementController { protected override ITenantStore CreateTenantStore() { var storageAccount = ApplicationConfiguration.GetStorageAccount("DataConnectionString"); var tenantSQLTable = ... var logosSQLTable = .... return new SQLTenantStore(tenantSQLTable, logosSQLTable); } ... }
The factory method pattern enables you to modify the behavior of a class without modifying the class itself by using inheritance.
使用继承,工厂方法允许你修改类的行为,而无需修改类本身。
The application can use the SQLManagementController class to use a SQL-based store without you needing to make any changes to the original ManagementController class. This approach results in a flexible and extensible design and implements the open/closed principle described in the previous chapter. However, it does not result in a maintainable solution because all the client classes that use the TenantStore class are still responsible for instantiating TenantStore instances correctly and consistently.
这个应用可以使用SQLManagementController 类去使用SQLbased 存储,没有在原ManagementController类中做任何修改。这种方法导致灵活和可扩展的设计,并实现前一章中描述的开/闭原理。但是,它不会产生可维护的解决方案,因为使用TenantStore 类所有的客户类仍然负责正确和一致的实例化TenantStore 实例。
It is also still difficult to test the ManagementController class because it depends on the TenantStore type, which in turn is tied to specific storage types (FilesContainer and EntitiesContainer). One approach to testing would be to create a MockManagementController type that derives from Management-Controller and that uses a mock storage implementation to return test data: in other words you must create two mock types to manage the testing.
ManagementController 类的测试仍然是困难的,因为它依赖TenantStore 类型,而这又依赖于特定的存储类型(FilesContainer 和EntitiesContainer)。一种测试方法是创建一个来自ManagementController的MockManagementController类型,并使用模拟存储实现返回测试数据:换句话说,您必须创建两个模拟类型来管理测试。
In this example, there is an additional complication because of the way that ASP.NET MVC locates controllers and views based on a naming convention: you must also update the MVC routes to ensure that MVC uses the new SQLManagementController class.
在这个示例中,还有一个额外的复杂性,因为ASP.NET MVC基于命名约定定位控制器和视图的方式:你不洗还要修改MVC的路由去保证MVC使用新的SQLManagementController 类。
Simple Factory Pattern
简单工厂模式
While the factory method pattern does not remove the dependencies from the high-level client class, such as the ManagementController class, on the low-level class, you can achieve this with the simple factory pattern. In this example, you can see that a new factory class named TenantStoreFactory is now responsible for creating the TenantStore instance on behalf of the ManagementController class.
虽然工厂方法模式不能从底层类中移除高层客户类(ManagementController 类)的依赖性,但可以使用简单的工厂模式实现此目的。在这个示例中,你可以看到名为TenantStoreFactory 的新工厂类,现在负责代表ManagementController 类创建TenantStore 的实例。
public class ManagementController : Controller { private readonly ITenantStore tenantStore; public ManagementController() { var tenantStoreFactory = new TenantStoreFactory(); this.tenantStore = tenantStoreFactory.CreateTenantStore(); } public ActionResult Index() { var model = new TenantPageViewData<IEnumerable<string>>(this.tenantStore.GetTenantNames()) { Title = "Subscribers" }; return this.View(model); } ... }
The simple factory pattern removes the direct dependency of the Management-Controller class on a specific store implementation. Instead of including the code needed to build a TenantStore instance directly, the controller class now relies on the TenantStoreFactory class to create the instance on its behalf.
这个简单工厂模式移除了ManagementController 对特定存储实现的直接依赖。而不是直接包含构建TenantStore实例所需的代码,控制器类现在依赖TenantStoreFactory 类来代表其创建实例。
Abstract Factory Pattern
抽象工厂模式
One of the problems that can arise from using the simple factory pattern in a large application is that it can be difficult to maintain consistency. For example, the application may include multiple store classes such as SurveyStore, LogoStore, and ReportStore classes in addition to the TenantStore class you’ve seen in the examples so far. You may have a requirement to use a particular type of storage for all of the stores. Therefore, you could implement a BlobStoreFactory abstract factory class that can create multiple blob-based stores, and a SQLStoreFactory abstract factory class that can create multiple SQL based stores.
在大型应用程序中使用简单工厂模式可能产生的问题之一是可能难以保持一致性。例如,除了您在示例中看到的TenantStore类之外,应用程序还可以包括多个商店类,例如SurveyStore,LogoStore和ReportStore类。您可能需要为所有存储使用特定类型的存储。因此,你可以实现BlobStoreFactory 抽象工厂类创建多个基于blob的仓储,和实现SQLStoreFactory 抽象工厂类创建多个基于SQL的仓储。
The abstract factory pattern is described in “Design Patterns: Elements of Reusable Object-Oriented Software” by Gamma, et al.
抽象工厂模式在Gamma等人的“设计模式:可复用面向对象软件的原理”中描述。
The abstract factory pattern is useful if you have a requirement to create families of related objects in a consistent way.
如果您需要以一致的方式创建相关对象的族,则抽象工厂模式很有用。
Service Locator Pattern
服务定位模式
Using a service locator provides another variation to this general approach of using another class to create objects on your behalf. You can think of a service locator as a registry that you can look up an instance of an object or service that another class in your application created and registered with the service locator. The service locator might support querying for objects by a string key or by interface type. Often, in contrast to the factory patterns where the factory creates the object but gives responsibility for managing its lifetime to the client class, the service locator is responsible for managing the lifetime of the object and simply returns a reference to the client. Also, factories are typically responsible for creating instances of specific types or families of types as in the case of the abstract factory pattern, while a service locator may be capable of returning a reference to an object of any type in the application.
使用服务定位提供了一般方法使用另一个类地替代你创建对象的另一个变化。你可以认为服务定位就是注册表,那样你可以查找对象或服务的实例,应用程序中的另一个类创建并注册到服务定位器。服务定位也能支持根据字符串的主键或接口类型查询对象。通常,与工厂创建对象,但是客户代码负责管理对象的生命周期的工厂模式相反,服务定位负责管理对象的生命周期,只返回引用到客户端。此外,工厂通常负责创建特定类型或类型的实例的实例,如在抽象工厂模式的情况下,而服务定位器可以能够返回对应用中任何类型的对象的引用。
The section “Object Lifetime” later in this chapter discusses object lifetimes in more detail.
本节后面的“对象生命周期”部分更详细地讨论对象生命周期。
Any classes that retrieve object references or service references from the service locator will have a dependency on the service locator itself.
For a description of the service locator pattern, see the section “Using a Service Locator” in the article “Inversion of Control Containers and the Dependency Injection pattern” by Martin Fowler.
任何类从服务定位中检索对象引用或服务应用,都将依赖服务定位它自身。
服务定位模式的一个描述,请看Martin Fowler写的文章“Inversion of Control Containers and the Dependency Injection pattern”中的“Using a Service Locator”章节.
For a discussion of why the service locator may be considered an anti-pattern, see the blog post “Service Locator is an Anti-Pattern” by Mark Seeman.
For a shared interface for service location that application and framework developers can reference, see the Common Service Locator library. The library provides an abstraction over dependency injection containers and service locators. Using the library allows an application to indirectly access the capabilities without relying on hard references.
讨论为什么服务定位器可能被认为是一种反模式,请看Mark Seeman发表的博客“Service Locator is an Anti-Pattern”。
对于应用程序和框架开发人员可以引用的服务定位的共享接口,请参阅Common Service Locator库。库提供了对依赖注入容器和服务定位器的抽象。使用库允许应用程序间接访问功能而不依赖于硬引用。
When using a service locator, every class will have a dependency on your service locator. This is not the case with dependency injection.
当使用服务定位,每个类都将依赖你的服务定位。这不是依赖注入的情况。
Dependency Injection
依赖注入
A common feature of the all the factory patterns and the service locator pattern, is that it is still the high-level client object’s responsibility to resolve its own dependencies by requesting the specific instances of the types that it needs. They each adopt a pull model of varying degrees of sophistication, assigning various responsibilities to the factory or service locator. The pull model also means that the high-level client class has a dependency on the class that is responsible for creating or locating the object it wants to use. This also means that the dependencies of the high-level client classes are hidden inside of those classes rather specified in a single location, making them harder to test.
Figure 1 shows the dependencies in the simple factory pattern where the factory instantiates a TenantStoreobject on behalf of the ManagementController class.
所有工厂模式和服务定位器模式的一个共同特征是,它仍然需要高层客户对象负责通过请求它所需要的特定实体类型来解决它自己的依赖关系。它们各自采用不同程度的复杂度的拉模型,将不同的责任分配给工厂或服务定位器。拉取模型意味着高层客户类有负责创建或定位它想要使用的对象的依赖。这也意味着高级客户端类的依赖性隐藏在这些类中,而是在单个位置中指定,这使得它们更难以测试。
图形1显示简单工厂模式在代表ManagementController 类创建实例化TenantStore 对象的依赖关系。
图形1 在工厂模式中的依赖
Dependency injection takes the opposite approach, adopting a push model in place of the pull model. Inversion of Control is a term that’s often used to describe this push model and dependency injection is one specific implementation of the inversion of control technique.
Martin Fowler states: “With service locator the application class asks for it explicitly by a message to the locator. With injection there is no explicit request, the service appears in the application class—hence the inversion of control.” (Inversion of Control Containers and the Dependency Injection pattern.)
With dependency injection, another class is responsible for injecting (pushing) the dependencies into the high-level client classes, such as the Management-Controller class, at runtime. The following code sample shows what the high-level ManagementController class looks like if you decide to use dependency injection.
依赖注入采用相反的方法,采用推模型代替拉模型。控制反转是一个常用来描述推模型的术语,依赖注入是控制反转技术的一个具体实现。
Martin Fowler写明:“使用服务定位器,应用程序类通过到定位器的消息显式地请求它。 使用注入没有明确的请求,服务出现在应用程序类中 - 因此反转控制。”(Inversion of Control Containers and the Dependency Injection pattern.)
用依赖注入,另一个类负责在运行时注入(推送)依赖性到高层客户类,如Management-Controller类。以下代码示例显示了如果您决定使用依赖注入,高级ManagementController类的模样。
public class ManagementController : Controller { private readonly ITenantStore tenantStore; public ManagementController(ITenantStore tenantStore) { this.tenantStore = tenantStore; } public ActionResult Index() { var model = new TenantPageViewData<IEnumerable<string>>(this.tenantStore.GetTenantNames()) { Title = "Subscribers" }; return this.View(model); } ... }
As you can see in this sample, the ManagementController constructor receives an ITenantStore instance as a parameter, injected by some other class. The only dependency in the ManagementContoller class is on the interface type. This is better because it doesn’t have any knowledge of the class or component that is responsible for instantiating the ITenantStore object.
In Figure 2, the class that is responsible for instantiating the TenantStore object and inserting it into the ManagementController class is called the Dependency-InjectionContainer class.
正如你所看到的这个示例,ManagementController 构造函数接收一个ITenantStore 实例参数,由其他类注入。ManagementContoller 类仅仅依赖一个接口类型。这样是更好地,因为它不需要有负责实例化ITenantStore对象的类或组件的任何知识。
在图形2中,负责实例化TenantStore对象并将其插入到ManagementController类中的类称为DependencyInjectionContainer类。
图形2 当使用依赖注入的依赖关系
Chapter 3, “Dependency Injection with Unity,” will describe in more detail what happens in the DependencyInjectionContainer class
章节3,“用Unity依赖注入”,将更加详细的描述DependencyInjectionContainer 类中发生了什么。
The key difference between the Figure 1 and Figure 2 is the direction of the dependency from the ManagementController class. In Figure 2, the only dependency the ManagementController class has is on the ITenantStore interface.
图形1和图形2的关键差别在来自ManagementController 类的依赖方向上。图形2中ManagementController 类仅仅依赖ITenantStore 接口。
Object Composition
对象组成
So far in this chapter, you have seen how dependency injection can simplify classes such as the ManagementController class and minimize the number of dependencies between classes in your application. The previous chapter explained some of the benefits of this approach, such as maintainability and testability, and showed how this approach relates to the SOLID principles of object-oriented programming. You will now see how this might work in practice: in particular, how and where you might use dependency injection in your own applications.
到本章目前为止,你已经看到依赖注入如何简化类,如ManagementController类,并最小化应用程序中的类之间的依赖关系的数量。前一章解释了一些这种方法的好处,比如可维护性和可测试性,并且展示了该方法如何与SOLID原则的面向对象编程相关。你现在可以看到这在现实中如何可以工作的:尤其是,在你自己的应用程序中你可以使用依赖注入的方式和位置。
In Figure 2, the DependencyInjectionContainer class may manage the dependencies of multiple high level client classes such as the Management-Controller class on multiple service classes such as the TenantStoreclass.
You can use either a dependency injection container or implement dependency injection manually using factories. As you’ll see in the next chapter, using a container is easier and provides additional capabilities such as lifetime management, interception, and registration by convention.
在图形2中,DependencyInjectionContainer 类可以管理高层客户类的多个依赖,例如ManagementController 类上有多个服务类,比如TenantStore 类。
你可以你可以使用依赖注入容器或者使用工厂手工实现依赖注入。正如你在下已章节看到的,使用容器是更简单的,并提供附加功能,如生命周期管理、拦截和约定注册。
If you adopt the dependency injection approach, you will have many classes in your application that require some other class or component to pass the necessary dependencies into their constructors or methods as parameters or as property values before you can use them. This implies that your application requires a class or component that is responsible for instantiating all the required objects and passing them into the correct constructors, methods, and properties: your application must know how to compose its object graph before it can perform any work. This must happen very early in the application’s lifecycle: for example, in the Mainmethod of a console application, in the Global.asax in a web application, in a role’s OnStart method in a Windows Azure application, or in the initialization code for a test method.
如果你采用依赖注入方式,在你的应用程序中你将有很多类,需要一些其他类或组件将必要的依赖性传递到构造函数或方法作为参数或属性值,然后你才能使用它们。这就以为这你的应用程序需要一个类或组件是负责实例化所有必需的对象并将它们传递到正确的构造函数,方法和属性:你的应用程序必须知道如何如何去组成他的对象图,在可以执行任何工作前。这必须在应用程序的生命周期的早期发生:比如,在控制台程序的Main方法中,Web应用程序的Global.asax中 ,Windows Azure 应用程序中的角色的OnStart 方法中,或者在测试方法的初始化代码中。
Typically, you should place all the code tells the application how to build its object graph in a single location; this is known as the Composition Root pattern. This makes it much easier to maintain and update the application.
通常,您应该将所有代码告诉应用程序如何在单个位置构建其对象图; 这被称为组合根模式。这使得应用程序维护和更新更加简单。
Object Lifetime
对象生命周期
You should determine when to create the objects in your application based on criteria such as which object is responsible for managing the state, is the object shared, and how long the object will live for. Creating an object always takes a finite amount of time that is determined by the object’s size and complexity, and once you have created an object, it occupies some of your system’s memory.
在你的应用程序中你应该基于原则(如哪个对象负责管理状态、是否共享对象以及对象可以存活多久)决定何时去创建对象。创建对象总是需要一定量的时间,由对象的大小和复杂性决定,一旦创建了对象,它就占用了系统的一些内存。
In the example, you’ve seen in this chapter, there is a single Management-Controller client class that uses an implementation of the ITenantStore interface. In a real application, there may be many other client classes that all need ITenantStore instances. Depending on the specific requirements and structure of your application, you might want each client class to have its own ITenantStore object, or have all the client classes share the same ITenantStore instance, or for different groups of client classes each have their own ITenantStore instance.
在本章的例子中你已经看到,有一个ManagementController客户类使用一个ITenantStore 的实例。在现实的应用程序中,那里可能有许多其他客户类都需要ITenantStore 的实例。根据你应用程序的特殊要求和数据结构,你可能想要每个客户类都有自己的ITenantStore 对象,或偶有的客户类共享相同的ITenantStore 实例,或每个不同的客户类组有自己的ITenantStore 实例。
If every client object has its own ITenantStore instance, then the ITenantStore instance can be garbage collected along with the client object. If multiple client objects share an ITenantStore instance, then the class or component that instantiates the shared ITenantStore object must responsible for tidying it up when all the clients are finished with it.
如果每个客户对象都有自己的ITenantStore 实例,则ITenantStore实例可以与客户端对象一起进行垃圾回收。如果多个客户对象共享一个ITenantStore 实例,则类或组件实例化共享的ITenantStore 对象必须负责在所有客户端完成它后对其进行整理。
Whichever way you create an object, there is always a trade-off between performance and resource utilization when you decide where to instantiate it.
无论以何种方式创建对象,在决定在哪里实例化它时,在性能和资源利用率之间总是存在权衡。
Types of Injection
注入类型
Typically, when you instantiate an object you invoke a class constructor and pass any values that the object needs as parameters to the constructor. In the example that you saw earlier in this chapter, the constructor in the Management-Controller class expects to receive an object that implements the ITenantStore interface. This is an example of constructor injection and is the type of injection you will use most often. There are other types of injection such as property setter injection and method call injection, but they are less commonly used.
通常,当您实例化一个对象时,您调用一个类构造函数,并将该对象需要的任何值作为参数传递给构造函数。在本章前面的例子中你看到,ManagementController 类中的构造函数需要接收实现了ITenantStore 接口的对象。这是一个构造函数注入的例子,是你最常使用的注入类型。还有其他的一些注入类型,比如属性设置注入和方法调用注入,但是他们一般使用的比较少。
Property Setter Injection
属性设置注入
As an alternative or in addition to passing a parameter to a constructor, you may want to set a property value when you instantiate an object in your application. The following code sample shows part of a class named AzureTable in an application that uses property injection to set the value of the ReadWrite-Strategy property when it instantiates AzureTable object.
作为向构造函数传递参数的替代方法或补充方法,您可能需要在应用程序中实例化对象时设置属性值。以下代码示例显示了应用程序中名为AzureTable的类的一部分,在实例化AzureTable对象时,它使用属性注入来设置ReadWriteStrategy属性的值。
public class AzureTable<T> : ... { public AzureTable(StorageAccount account): this(account, typeof(T).Name) { } ... public IAzureTableRWStrategy ReadWriteStrategy{ get; set; } ... }
Notice that the constructors are not responsible for setting the read/write strategy and that the type of the ReadWriteStrategy property is an interface type. You can use property setter injection to provide an instance of the IAzureTable-RWStrategy type when your dependency injection container constructs an instance of AzureTable<T>.
注意,这个构造函数不负责设置读/写策略,并且ReadWriteStrategy 属性的类型是接口类型。当你的依赖注入容器构造AzureTable<T>实例时,可以使用属性setter注入来提供IAzureTableRWStrategy类型的实例。
You should only use property setter injection if the class has a usable default value for the property. While you cannot forget to call a constructor, you can forget to set a property such as the ReadWriteStrategy property in the example above.
如果类具有可用的属性的默认值,那么应该仅使用属性setter注入。虽然你不能忘记调用构造函数,但你可以忘记在上面的例子中设置一个属性,如ReadWriteStrategy属性。
You should use property setter injection when the dependency is optional. However don’t use property setter injection as a technique to avoid polluting your constructor with multiple dependencies; too many dependencies might be an indicator of poor design because it is placing too much responsibility in a single class. See the single responsibility principle discussed in Chapter 1.
当依赖是可选的,你应该使用属性setter注入。但是,不要使用属性setter注入作为一种技术,以避免污染具有多个依赖的构造函数; 太多的依赖性可能是设计不良的一个指标,因为它在一个类中放置了太多的责任。见第1章中讨论的单一责任原则。
However, dependencies are rarely optional when you are building a LOB application. If you do have an optional dependency, consider using constructor injection and injecting an empty implementation (the Null Object Pattern.)
然而当你构建LOB应用程序,依赖很少是可选的。如果你有一个可选的依赖项,考虑使用构造函数注入和注入一个空的实现(空对象模式)。
Method Call Injection
方法调用注入
In a similar way to using property setter injection, you might want to invoke a method when the application instantiates an object to perform some initialization that is not convenient to perform in a constructor. The following code sample shows part of a class named MessageQueue in an application that uses method injection to initialize the object.
以类似的方式使用属性setter注入,当应用程序实例化一个对象以执行一些不方便在构造函数中执行的初始化时,您可能想要调用一个方法。下面实例代码显示了MessageQueue 类的部分代码,在应用程序中使用方法注入初始化对象。
public class MessageQueue<T> : ... { ... public MessageQueue(StorageAccount account): this(account, typeof(T).Name.ToLowerInvariant()) { } public MessageQueue(StorageAccount account, string queueName) { ... } public void Initialize(TimeSpan visibilityTimeout,IRetryPolicyFactory retryPolicyFactory) { ... } ... }
In this example, the Initialize method has one concrete parameter type and one interface parameter type. You can use method injection to provide an instance of the IRetryPolicyFactory type when your dependency injection container constructs an instance of MessageQueue<T>.
Method call injection is useful when you want to provide some additional information about the context that the object is being used in that can’t be passed in as a constructor parameter.
在这个例子中,Initialize 方法有个具体参数类型和一个接口参数类型。当依赖注入容器构造MessageQueue<T>实例时,可以用方法注入来提供IRetryPolicyFactory 类型的实例。
当您想要提供一些有关上下文的额外信息,所使用的对象不能作为构造函数参数传递,方法调用注入是有效的。
When You Shouldn’t Use Dependency Injection
何时你不应该使用依赖注入
Dependency injection is not a silver bullet. There are reasons for not using it in your application, some of which are summarized in this section.
• Dependency injection can be overkill in a small application, introducing additional complexity and requirements that are not appropriate or useful.
• In a large application, it can make it harder to understand the code and what is going on because things happen in other places that you can’t immediately see, and yet they can fundamentally affect the bit of code you are trying to read. There are also the practical difficulties of browsing code like trying to find out what a typical implementation of the ITenantStore interface actually does. This is particularly relevant to junior developers and developers who are new to the code base or new to dependency injection.
• You need to carefully consider if and how to introduce dependency injection into a legacy application that was not built with inversion of control in mind. Dependency injection promotes a specific style of layering and decoupling in a system that may pose challenges if you try to adapt an existing application, especially with an inexperienced team.
• Dependency injection is far less important in functional as opposed to object-oriented programming. Functional programming is becoming a more common approach when testability, fault recovery, and parallelism are key requirements.
• Type registration and resolving do incur a runtime penalty: very negligible for resolving, but more so for registration. However, the registration should only happen once.
依赖注入不是高招。在你的应用程序中没有使用它的原因,其中一些在本节中总结。
- 依赖注入在小型应用程序中可能是过度的,引入了不适当或无用的额外复杂性和需求。
-
在大型应用程序中,它使得代码更难理解和是什么,因为事物发身在其他位置,你不能直接看到,并且它们还可能从根本上影响您尝试阅读的一些代码。
另外还有一些阅读代码的实际的困难,比如尝试找出ITenantStore 接口实际上的标准实现。这对于那些对代码库或新的依赖注入新手的初级开发人员和开发人员尤其相关。 - 您需要仔细考虑是否以及如何将依赖注入引入一个不是以反转控制构建的遗留应用程序。如果依赖注入在系统中促进特定样式的分层和解耦,如果您尝试适应现有应用程序,尤其是没有经验的团队,可能会带来挑战。
- 依赖注入在反功能的面向对象编程是不重要的。当可测试性,故障恢复和并行性是关键要求时,功能编程正成为更常见的方法。
- 类型注册和解析会导致运行时惩罚:对于解析是非常微不足道的,但是更多的是对于注册。但是,注册只应发生一次。
Programming languages shape the way we think and the way we code. For a good exploration of the topic of dependency injection when the functional programming model is applied, see the article “Dependency Injection Without the Gymnastics” by Tony Morris.
According to Mark Seeman, using dependency injection “can be dangerous for your career because it may increase your overall knowledge of good API design. Once you learn how proper loosely coupled code can look like, it may turn out that you will have to decline lots of job offers because you would otherwise have to work with tightly coupled legacy apps.”
What are the downsides to using Dependency Injection? On StackOverflow.
编程语言塑造我们的思维方式和我们的代码的方式。 对于应用函数编程模型时依赖注入的主题的良好探索,请参阅Tony Morris的文章“Dependency Injection Without the Gymnastics”。根据Mark Seeman,使用依赖注入“对你的职业来说可能是危险的,因为它可能增加你对良好的API设计的整体知识。一旦你学习了如何适当的松散耦合的代码可能会发现,你将不得不拒绝大量的工作机会,因为你将不得不与紧密耦合的遗留应用程序。”