3

如果我有一个带有服务的类,我希望所有派生类都可以访问(比如安全对象或存储库),那么我可能会执行以下操作:

public abstract class A
{
    static ISecurity _security;
    public ISecurity Security { get { return _security; } }

    public static void SetSecurity(ISecurity security) { _security = security; }
}

public class Bootstrapper
{
    public Bootstrapper()
    {
        A.SetSecurity(new Security());
    }
}

似乎最近我看到到处都在回避静态属性,这是绝对要避免的。对我来说,这似乎比在我创建的每个派生类的构造函数中添加一个 ISecurity 参数更干净。鉴于我最近阅读的所有内容,我想知道:

这是一个可接受的依赖注入应用程序,还是我违反了一些可能会在以后困扰我的主要设计原则?此时我没有进行单元测试,所以如果我这样做的话,我可能会突然意识到我的问题的答案。老实说,虽然我可能不会改变我的设计,但如果有其他重要原因我应该改变它,那么我很可能会。

编辑:我第一次编写该代码时犯了几个愚蠢的错误……现在已修复。只是想我会指出这一点,以防有人碰巧注意到:)

编辑:SWeko 提出了一个很好的观点,即所有派生类都必须使用相同的实现。在我使用这种设计的情况下,该服务始终是单例的,因此它有效地执行了已经存在的需求。当然,如果不是这样,这将是一个糟糕的设计。

4

2 回答 2

2

由于几个原因,这种设计可能存在问题。

您已经提到了单元测试,这非常重要。这种静态依赖会使测试变得更加困难。当假货必须是空对象ISecurity实现以外的任何东西时,您会发现自己必须在测试拆除时删除假货实现。在测试拆卸过程中将其删除可防止在您忘记删除该假对象时影响其他测试。测试拆解使您的测试更加复杂。没那么复杂,但是当许多测试都拆除代码时,这会增加,当一个测试忘记运行拆除时,您将很难在测试套件中找到错误。您还必须确保已注册ISecurityfake 对象是线程安全的,不会影响其他可能并行运行的测试(测试框架,如 MSTest 出于明显的性能原因并行运行测试)。

将依赖项作为静态注入的另一个可能的问题是,您强制此ISecurity依赖项成为单例(并且可能是线程安全的)。例如,这不允许应用与单例具有不同生活方式的任何拦截器和装饰器

另一个问题是,从构造函数中删除此依赖项会禁用任何可以由 DI 框架代表您完成的分析或诊断。由于您手动设置了此依赖项,因此框架不知道此依赖项。从某种意义上说,您将管理依赖关系的责任移回应用程序逻辑,而不是让组合根控制依赖关系连接在一起的方式。现在应用程序必须知道这ISecurity实际上是线程安全的。这是通常属于组合根的责任。

您希望将此依赖项存储在基本类型中的事实甚至可能表明违反了一般设计原则:单一责任原则(SRP)。它与我过去犯的设计错误有些相似。我有一组全部继承自基类的业务操作。这个基类实现了各种行为,例如事务管理、日志记录、审计跟踪、添加容错以及....添加安全检查。这个基类变成了一个无法管理的神物。它难以管理,只是因为它有太多的责任;它违反了 SRP。如果你想了解更多,这是我的故事。

因此,与其在基类中实现这种安全问题(它可能是一个横切问题),不如尝试将基类全部移除并使用装饰器为这些类添加安全性。您可以用一个或多个装饰器包装每个类,每个装饰器可以处理一个特定的问题。这使得每个装饰器类都易于遵循,因为它们将遵循 SRP。

于 2013-01-31T13:58:34.993 回答
1

问题是这并不是真正的依赖注入,即使它被封装在类的定义中。诚然,

static Security _security;

会比 更糟糕Security,但仍然A无法使用调用者传递给它们的任何安全性,它们需要依赖于静态属性的全局设置。

我想说的是,您的用法与以下内容没有什么不同:

public static class Globals
{
   public static ISecurity Security {get; set;}
}    
于 2013-01-31T12:22:20.903 回答