我正在尝试提高IoC容器的性能。我们正在使用Unity和SimpleInjector,并且有一个带有此构造函数的类:
public AuditFacade(
IIocContainer container,
Func<IAuditManager> auditManagerFactory,
Func<ValidatorFactory> validatorCreatorFactory,
IUserContext userContext,
Func<ITenantManager> tenantManagerFactory,
Func<IMonitoringComponent> monitoringComponentFactory)
: base(container, auditManagerFactory, GlobalContext.CurrentTenant,
validatorCreatorFactory, userContext, tenantManagerFactory)
{
_monitoringComponent = new Lazy<IMonitoringComponent>(monitoringComponentFactory);
}
我也有这个构造函数的另一个类:
public AuditTenantComponent(Func<IAuditTenantRepository> auditTenantRepository)
{
_auditTenantRepository = new Lazy<IAuditTenantRepository>(auditTenantRepository);
}
我看到大多数情况下,第二个问题在1毫秒内得到解决,而第一个问题平均需要50-60毫秒。我敢肯定,速度较慢的原因是由于参数,它具有更多的参数。但是,如何改善这种速度较慢的服务器的性能呢?我们使用Func<T>
作为参数是事实吗?如果它导致速度缓慢,该怎么办?
参考方案
您当前的设计可能还有很多改进之处。这些改进可以分为五个不同的类别,即:
可能滥用基类
使用服务定位器反模式
使用环境上下文反模式
泄漏的抽象
在注入构造器中做得太多
可能滥用基类
普遍的共识是您应该选择composition over inheritance。与使用组合相比,继承经常被过度使用,并且通常会增加更多的复杂性。通过继承,派生类与基类实现紧密耦合。我经常看到一个基类被用作实用工具类,其中包含各种辅助方法,这些方法可以解决某些派生类可能需要的横切关注点和其他行为。
通常更好的方法是一起删除所有基类,然后将服务注入到只暴露服务所需功能的实现中(在您的情况下为AuditFacade
类)。或者,如果涉及到横切关注点,则根本不要注入该行为,而应使用decorator包装实现,以扩展具有横切关注点的类的行为。
在您的情况下,我认为复杂性显然正在发生,因为实现中未使用7个注入的依赖中的6个,而是仅将其传递给基类。换句话说,这6个依赖项是基类的实现细节,而实现仍然被迫了解它们。通过在服务之后抽象该基类(的一部分),可以将AuditFacade
需要的依赖关系数量减少到两个依赖关系:Func<IMonitoringComponent>
和新抽象。该抽象背后的实现将有6个构造函数依赖项,但是AuditFacade
(和其他实现)对此没有注意。
使用服务定位器反模式
AuditFacade
依赖于IIocContainer
抽象,这非常类似于Service Locator pattern的实现。 Service Locator should be considered an anti-pattern因为:
它隐藏了一个类的依赖关系,从而导致运行时错误而不是
编译时错误,以及使代码更难处理
维护,因为不清楚何时会引入
突破性的变化。
总有更好的选择,可以将容器或容器上的抽象注入到应用程序代码中。请注意,有时您可能希望将容器注入工厂实现中,但是只要将它们放置在Composition Root内,就不会有任何危害,因为Service Locator is about roles, not mechanics。
使用环境上下文反模式
静态GlobalContext.CurrentTenant
属性是Ambient Context反模式的实现。 Mark Seemann,我在our book中写到这种模式:
“环境上下文”的问题与“服务”的问题有关
定位器。主要问题是:
DEPENDENCY隐藏。
测试变得更加困难。
根据其上下文更改DEPENDENCY变得非常困难。 [第5.3.3段]
这种情况下的使用确实是很奇怪的IMO,因为您是从构造函数内部的某个静态属性中获取当前租户的,并将其传递给基类。为什么基类本身不调用该属性?
但是没有人应该称其为静态属性。这些静态属性的使用使您的代码难以阅读和维护。它使单元测试更加困难,并且由于您的代码库通常会被此类静态调用所困扰,因此它成为隐藏的依赖项;它具有与使用Service Locator相同的缺点。
泄漏的抽象
Leaky Abstraction是Dependency Inversion Principle违反,其中抽象违反了原则的第二部分,即:
B.抽象不应依赖细节。细节应取决于
抽象。
尽管Lazy<T>
本身不是抽象(Lazy<T>
是具体类型),但是当用作构造函数参数时,它可能会成为泄漏抽象。例如,如果您直接注入Lazy<IMonitoringComponent>
而不是IMonitoringComponent
(这基本上是您在代码中所做的事情),则新的Lazy<IMonitoringComponent>
依赖项会泄漏实现细节。此Lazy<IMonitoringComponent>
向消费者传达所使用的IMonitoringComponent
实现创建起来昂贵或耗时。但是消费者为什么要关心这个呢?
但是与此有关的还有更多问题。如果在某个时间点使用的IUserContext
实现的创建成本很高,则必须开始在整个应用程序中进行全面更改(违反Open/Closed Principle),因为所有IUserContext
依赖项都需要更改为Lazy<IUserContext>
并且必须将该IUserContext
的所有使用者更改为使用userContext.Value.
。而且,您还必须更改所有单元测试。如果您忘记将一个IUserContext
引用更改为Lazy<IUserContext>
,或者在创建新类时不小心依赖了IUserContext
,会发生什么情况?您的代码中有一个错误,因为这时立即创建了用户上下文实现,这会导致性能问题(这会引起问题,因为这就是您首先使用Lazy<T>
的原因)。
那么,为什么我们要对代码库进行彻底的更改,并用额外的间接层污染它呢?没有任何理由。创建依赖项成本很高的事实是实现细节。您应该将其隐藏在抽象后面。这是一个例子:
public class LazyMonitoringComponentProxy : IMonitoringComponent {
private Lazy<IMonitoringComponent> component;
public LazyMonitoringComponentProxy(Lazy<IMonitoringComponent> component) {
this.component = component;
}
void IMonitoringComponent.MonitoringMethod(string someVar) {
this.component.Value.MonitoringMethod(someVar);
}
}
在此示例中,我们将Lazy<IMonitoringComponent>
隐藏在proxy class后面。这使我们可以用此IMonitoringComponent
替换原始的LazyMonitoringComponentProxy
实现,而不必对其余应用程序进行任何更改。使用Simple Injector,我们可以按以下方式注册此类型:
container.Register<IMonitoringComponent>(() => new LazyMonitoringComponentProxy(
new Lazy<IMonitoringComponent>(container.GetInstance<CostlyMonitoringComp>));
正如Lazy<T>
可能被用作泄漏抽象一样,Func<T>
也是如此,尤其是出于性能原因而这样做时。正确应用DI时,大多数时候不需要将工厂抽象注入代码中,例如Func<T>
。
请注意,如果要在各处注入Lazy<T>
和Func<T>
,则会使代码库变得不必要。
在注入构造器中做得太多
但是,除了Lazy<T>
和Func<T>
是泄漏抽象之外,您实际上非常需要它们的事实表明您的应用程序存在问题,因为Injection Constructors should be simple。如果构造函数需要很长时间才能运行,则您的构造函数会做太多事情。构造器逻辑通常很难测试,并且如果这样的构造器调用数据库或从HttpContext请求数据,则verification of your object graphs变得更加困难,以至于您可能一起跳过验证。跳过对对象图的验证是一件很糟糕的事情,因为这迫使您单击整个应用程序以了解您的DI容器是否配置正确。
我希望这能给您一些有关改进班级设计的想法。
将谓词<T>转换为Func <T,bool> - c#我有一个包含成员Predicate的类,希望在Linq表达式中使用该类:using System.Linq; class MyClass { public bool DoAllHaveSomeProperty() { return m_instrumentList.All(m_filterExpression); } private IEnumerable&…
合并List <T>和List <Optional <T >> - java鉴于: List<Integer> integers = new ArrayList<>(Arrays.asList( 10, 12 )); List<Optional<Integer>> optionalIntegers = Arrays.asList( Optional.of(5), Optional.em…
无法从ArrayList <String>转换为List <Comparable> - java当我写下面的代码时,编译器说 无法从ArrayList<String>转换为List<Comparable>private List<Comparable> get(){ return new ArrayList<String>(); } 但是当我用通配符编写返回类型时,代码会编译。private List&l…
OpenShift构建错误:无法在多模块Maven Spring启动项目的父模块中导入子模块类 - java我有一个使用spring的多模块Maven项目。通用模块类用作业务模块项目中的直接导入。我可以在本地PC上编译并成功运行它们。当我在OpenShift中部署相同的模块时,出现错误,无法在业务模块中导入通用模块类。项目结构可以总结如下:项目根 通用模块 src pom.xml 业务模块 src pom.xml pom.xml父POM:<?xml vers…
List <Dog>是List <Animal>的子类吗?为什么Java泛型不是隐式多态的? - java我对Java泛型如何处理继承/多态感到困惑。假设以下层次结构-动物(父母)狗-猫(儿童)因此,假设我有一个方法doSomething(List<Animal> animals)。根据继承和多态性的所有规则,我假设List<Dog>是List<Animal>,而List<Cat>是List<Animal&g…