- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试提高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);
}
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容器是否配置正确。
关于c# - 构造函数中的Func <T>参数是否会减慢我的IoC解析速度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26125371/
我有一个 if 语句,如下所示 if (not(fullpath.lower().endswith(".pdf")) or not (fullpath.lower().endswith(tup
然而,在 PHP 中,可以: only appears if $foo is true. only appears if $foo is false. 在 Javascript 中,能否在一个脚
XML有很多好处。它既是机器可读的,也是人类可读的,它具有标准化的格式,并且用途广泛。 它也有一些缺点。它是冗长的,不是传输大量数据的非常有效的方法。 XML最有用的方面之一是模式语言。使用模式,您可
由于长期使用 SQL2000,我并没有真正深入了解公用表表达式。 我给出的答案here (#4025380)和 here (#4018793)违背了潮流,因为他们没有使用 CTE。 我很欣赏它们对于递
我有一个应用程序: void deleteObj(id){ MyObj obj = getObjById(id); if (obj == null) { throw n
我的代码如下。可能我以类似的方式多次使用它,即简单地说,我正在以这种方式管理 session 和事务: List users= null; try{ sess
在开发J2EE Web应用程序时,我通常会按以下方式组织我的包结构 com.jameselsey.. 控制器-控制器/操作转到此处 服务-事务服务类,由控制器调用 域-应用程序使用的我的域类/对象 D
这更多是出于好奇而不是任何重要问题,但我只是想知道 memmove 中的以下片段文档: Copying takes place as if an intermediate buffer were us
路径压缩涉及将根指定为路径上每个节点的新父节点——这可能会降低根的等级,并可能降低路径上所有节点的等级。有办法解决这个问题吗?有必要处理这个吗?或者,也许可以将等级视为树高的上限而不是确切的高度? 谢
我有两个类,A 和 B。A 是 B 的父类,我有一个函数接收指向 A 类型类的指针,检查它是否也是 B 类型,如果是将调用另一个函数,该函数接受一个指向类型 B 的类的指针。当函数调用另一个函数时,我
有没有办法让 valgrind 使用多个处理器? 我正在使用 valgrind 的 callgrind 进行一些瓶颈分析,并注意到我的应用程序中的资源使用行为与在 valgrind/callgrind
假设我们要使用 ReaderT [(a,b)]超过 Maybe monad,然后我们想在列表中进行查找。 现在,一个简单且不常见的方法是: 第一种可能性 find a = ReaderT (looku
我的代码似乎有问题。我需要说的是: if ( $('html').attr('lang').val() == 'fr-FR' ) { // do this } else { // do
根据this文章(2018 年 4 月)AKS 在可用性集中运行时能够跨故障域智能放置 Pod,但尚不考虑更新域。很快就会使用更新域将 Pod 放入 AKS 中吗? 最佳答案 当您设置集群时,它已经自
course | section | type comart2 : bsit201 : lec comart2 :
我正在开发自己的 SDK,而这又依赖于某些第 3 方 SDK。例如 - OkHttp。 我应该将 OkHttp 添加到我的 build.gradle 中,还是让我的 SDK 用户包含它?在这种情况下,
随着 Rust 越来越充实,我对它的兴趣开始激起。我喜欢它支持代数数据类型,尤其是那些匹配的事实,但是对其他功能习语有什么想法吗? 例如标准库中是否有标准过滤器/映射/归约函数的集合,更重要的是,您能
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 9 年前。 Improve
我一直在研究 PHP 中的对象。我见过的所有示例甚至在它们自己的对象上都使用了对象构造函数。 PHP 会强制您这样做吗?如果是,为什么? 例如: firstname = $firstname;
...比关联数组? 关联数组会占用更多内存吗? $arr = array(1, 1, 1); $arr[10] = 1; $arr[] = 1; // <- index is 11; does the
我是一名优秀的程序员,十分优秀!