如果我有一个带有服务的类,我希望所有派生类都可以访问(例如,一个安全对象或一个存储库),那么我可以这样做:

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对必须使用相同实现的所有派生类提出了一个好的观点。在我使用这种设计的情况下,该服务始终为单例,因此它有效地执行了已经存在的要求。自然,如果不是这种情况,那将是一个糟糕的设计。

最佳答案

这种设计可能有很多问题。

您已经提到了单元测试,这很重要。这种静态依赖性会使测试变得更加困难。当伪造的ISecurity必须不是Null Object实施方案时,您将发现自己必须在测试拆除时删除该伪造的实施方案。在测试拆除过程中将其删除可防止您忘记删除该假对象时影响其他测试。拆解测试会使您的测试更加复杂。并没有那么复杂,但是当许多测试都包含拆卸代码时,这种情况加起来,并且当一个测试忘记运行拆卸时,您将很难在测试服中发现错误。您还必须确保注册的ISecurity伪造对象是线程安全的,并且不会影响可能并行运行的其他测试(出于明显的性能原因,诸如MSTest之类的测试框架并行运行测试)。

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

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

您想要以基本类型存储此依赖项的事实甚至可能表明违反了一般设计原则:Single Responsibility Principle(SRP)。它与我过去犯的设计错误有些相似。我有一组业务操作,所有这些操作都是从基类继承的。该基类实现了各种行为,例如事务管理,日志记录,审计跟踪,添加容错以及添加安全检查。该基类成为难以处理的God Object。它之所以难以管理,仅仅是因为它承担了太多的责任。它违反了SRP。 Here's my story如果您想进一步了解这一点。

因此,与其在基类中实现此安全性关注点(可能是跨领域的关注点),不如尝试一起删除所有基类并使用装饰器为这些类添加安全性。您可以用一个或多个装饰器包装每个类,并且每个装饰器可以处理一个特定的问题。这使得每个装饰器类都易于遵循,因为它们将遵循SRP。

10-08 06:50