Angular2 & WebApi (SPA) 用于企业应用 - 第 5 部分 - DI & IoC - 为什么和为什么不?






2.81/5 (22投票s)
在本文中,我们将根据我的实际情况,讨论使用 DI 和 IoC 的原因和不使用的原因。我并没有说 DI 不好。在我看来,每种技术都有其优点和缺点。因此,如果我们知道何时可以将哪种技术用于我们的业务,那就更好了。
系列中的其他文章
- 概述
- 添加新权限
- 项目结构
- 多语言 (i18n)
- DI & IoC - 为什么以及为什么不?
- RESTful & WebApi
- 管理应用生命周期
- 构建和部署应用
- TinyERP新版本(使用Angular 2 (typescript))
- CQRS:避免企业应用程序中的性能问题(基础)
- 多个数据存储:扩展你的存储库(第1部分)
- 多个数据存储:扩展你的存储库(第2部分)
- 基本身份验证(用户名/密码)与OWIN
引言
我在我的代码中查看了 IoC 和 DI。
希望这能帮助我们以良好的方式使用 IoC 和 DI。
我并没有说 DI 不好或者 IoC 比 DI 好。在我看来,每种技术都有其优点和缺点。因此,如果我们知道何时可以将哪种技术用于我们的业务,那就更好了。
如何获取代码
请在 https://github.com/techcoaching/TinyERP 查看代码。
DI - 依赖注入
DI 是一种很好的模式,可以帮助我们注入所需的引用。
class UserService : IUserService{ private IUserRepository userRepository; public UserService(IUserRepository userRepository) { this.userRepository = userRepository; } public void UserRepositoryForSomeActio(){ this.userRepository.<callSomeMethod>(); } }
如上面的代码所示,我们看到 userRepository 实例将被自动注入,它也已为我们在 UserRepositoryForSomeActio 方法准备好。
这很棒,因为我们可以专注于函数业务。
这在小型应用程序的上下文中是很好的。让我们看看在更复杂的应用程序类型(我现在指的是当今的企业应用程序)中是如何的。
在企业应用程序中,我们有很多核心对象/主要对象,它们依赖于其他对象。
例如:Order 对象,我们可以依赖于以下较小的对象
- 用户(创建订单的人)
- 订单项
- 类别
- 产品
- 升变
- 计量单位 (UOM)
- Branch
- ...
因此,OrderService 可能需要为所有依赖对象注入适当的存储库,如下面的代码所示。
class OrderService : IOrderService{ private IUserRepository userRepository; private IBranchRepository branchRepository; private IOrderLineRepository orderlineRepository; private IOrderCategoryRepository orderCategoryRepository; private IProductRepository productRepository; private IUOMRepository uomRepository; public UserService( IUserRepository userRepository, IBranchRepository branchRepository, IOrderLineRepository orderlineRepository, IOrderCategoryRepository orderCategoryRepository, IProductRepository productRepository, IUOMRepository uomRepository, ) { this.userRepository = userRepository; this.branchRepository = branchRepository; this.orderlineRepository = orderlineRepository; this.orderCategoryRepository = orderCategoryRepository; this.productRepository = productRepository; this.uomRepository = uomRepository; } public void UserRepositoryForSomeActio(){ this.userRepository.<callSomeMethod>(); } }
这里有几点我想提一下
- 我们需要定义私有变量列表来保存适当存储库的实例(例如:private IUOMRepository uomRepository;)
- 构造函数有很多参数。
- 构造函数的主体大部分是将注入的实例分配给私有参数(例如: this.userRepository = userRepository;)
这是我应用程序中使用依赖注入处理系统核心实体之一的真实代码。
namespace App.Services.Impl { public class DistributionListService : IDistributionListService { private IDistributionListRepository _distributionListRepository; private IDistributionPeriodRepository _distributionPeriodRepository; private IGeoRouteNorpostRepository _geoRouteNorpostRepository; private IUnaddressedOrderRepository _unaddressedOrderRepository; private IAddressedOrderSummaryPerRouteRepository _addressedOrderSummaryPerRouteRepository; private IOrderUnaddressedGeoRouteRepository _orderUnaddressedGeoRouteRepository; private IUserRepository _userRepository; private IVehicleRepository _vehicleRepository; private IGeoRoutePackageRepository _routePackageRepository; private IGeoRoutePackageRouteRepository _geoRoutePackageRouteRepository; private IDepartmentRepository _departmentRepository; private IAddressedOrderItemsDeliveryTimeRepository _addressedOrderItemsDeliveryTimeRepository; private ISortingTimeRepository _sortingTimeRepository; private IOrderRepository _orderRepository; private IDepartmentDistributionPeriodExecutionStatusRepository _distributionPeriodExecutionStatusRepository; private IDistributionPeriodService _distributionPeriodService; private IGeoRouteHouseholdService _geoRouteHouseholdService; public DistributionListService( IDistributionPeriodService distributionPeriodService, IGeoRoutePackageRouteRepository geoRoutePackageRouteRepository, IGeoRouteHouseholdService geoRouteHouseholdService, IDistributionListRepository distributionListRepository, IDistributionPeriodRepository distributionPeriodRepository, IGeoRouteNorpostRepository geoRouteNorpostRepository, IUnaddressedOrderRepository unaddressedOrderRepository, IAddressedOrderSummaryPerRouteRepository addressedOrderSummaryPerRouteRepository, IOrderUnaddressedGeoRouteRepository orderUnaddressedGeoRouteRepository, IUserRepository userRepository, IVehicleRepository vehicleRepository, IGeoRoutePackageRepository geoRoutePackageRepository, IDepartmentRepository departmentRepository, IAddressedOrderItemsDeliveryTimeRepository addressedOrderItemsDeliveryTimeRepository, ISortingTimeRepository sortingTimeRepository, IDepartmentDistributionPeriodExecutionStatusRepository departmentDistributionPeriodExecutionStatusRepository ) { //assign those injected to private variables } public void SomeMethod(){ // Use 3 of above repositories } } }
在这些服务类的指定方法中,我们只使用其中的一些(大部分大约 3 个)。
让我们想象一下首次调用上面“DistributionListService”的“SomeMethod”的执行流程。
- 系统查找 IDistributionListService 的实例
- 系统识别出此实现依赖于 15 个其他存储库。
- 这些存储库将被创建并作为参数传递给 DistributionListService 的构造函数。
- 系统调用“SomeMethod”并为其业务逻辑消耗其中的 3 个。
再次回顾,我们创建了 15 个存储库的实例。此过程可能需要注意为这些存储库创建其他依赖对象。
因此,在执行业务逻辑之前,需要创建大量的对象。
在 DI 中,我们可以管理实例的生命周期(在互联网上搜索“IoC”或“Inversion Of Control”),例如:Singleton、Transient、...
- 使用 Singleton
- 所有注册的类型都将创建一次。
- 下次,我们可以从内存中取回它。
在企业应用程序的上下文中,我们将有很多类始终存在于服务器内存中,只有其中一部分经常被使用。这意味着,我们没有充分利用服务器内存。
- 使用 Transient
- 所有注册的类型都将创建一次。
在企业应用程序的上下文中,我们将拥有许多依赖项很多的“核心对象/主要对象”(如上面的示例)。因此,创建此类实例需要大量其他依赖项的后续创建,并使用其中的一些。这意味着,我们没有充分利用服务器的强大功能。
IoC - 控制反转
为了改进上述问题,我决定在方法中使用 IoC。我的意思是,我们将根据需要解析和使用适当的存储库。
上面的代码可以更改为
namespace App.Services.Impl { public class DistributionListService : IDistributionListService { public DistributionListService() { //oh yeah, I'm free and can be removed from the class now } public void SomeMethod(){ IResitory1 repo1 = IoC.Resolve<IResitory1>(); IResitory2 repo2 = IoC.Resolve<IResitory2>(); IResitory3 repo3 = IoC.Resolve<IResitory3>(); // and use above repositories for logic business } } }
在这段代码中,我们可以看到
- 它看起来更漂亮了。
- 我们可以添加或删除未使用的依赖项,而不会影响其他代码。
- 我们解析我们需要使用的内容。默认情况下,所有这些存储库都将被释放。
因此,在这种方式下,我认为我们可以平衡企业应用程序上下文中的内存和服务器的性能。