【问题标题】:Practical Singleton & Dependency Injection question实用单例和依赖注入问题
【发布时间】:2010-09-19 19:18:39
【问题描述】:

假设我有一个名为 PermissionManager 的类,它在我的系统中应该只存在一次,并且基本上实现了为我的应用程序中的各种操作管理各种权限的功能。现在我的应用程序中有一些类需要能够在其方法之一中检查某个权限。此类的构造函数目前是公开的,即由 API 用户使用。

直到几周前,我还只是让我的班级在某处调用以下伪代码:

     PermissionManager.getInstance().isReadPermissionEnabled(this)

但是由于我注意到这里的每个人都讨厌单例+这种耦合,所以我想知道更好的解决方案是什么,因为我读到的反对单例的论点似乎是有道理的(不可测试,高耦合等) .

那么我真的应该要求 API 用户在类的构造函数中传入 PermissionManager 实例吗?即使我只希望我的应用程序存在一个 PermissionManager 实例?

或者我是否将这一切都搞错了,应该有一个非公共构造函数和一个工厂在某个地方为我传递 PermissionManager 的实例?


其他信息请注意,当我说“依赖注入”时,我指的是 DI Pattern...我没有使用任何 DI 框架,例如 Guice 或 Spring。 (……还)

【问题讨论】:

    标签: dependency-injection singleton


    【解决方案1】:

    如果您使用的是依赖注入框架,则处理此问题的常用方法是在构造函数中传入 PermissionsManager 对象,或者拥有框架为您设置的 PermissionsManager 类型的属性。

    如果这不可行,那么让用户通过工厂获取此类的实例是一个不错的选择。在这种情况下,工厂在创建类时将 PermissionManager 传递给构造函数。在您的应用程序启动时,您将首先创建单个 PermissionManager,然后创建您的工厂,传入 PermissionManager。

    您说得对,类的客户通常很难知道在哪里找到正确的 PermissionManager 实例并将其传递(甚至关心您的类使用 PermissionManager 的事实)。

    我见过的一个折衷解决方案是给你的类一个 PermissionManager 类型的属性。如果已设置属性(例如,在单元测试中),则使用该实例,否则使用单例。比如:

    PermissionManager mManager = null;
    public PermissionManager Permissions
    {
      if (mManager == null)
      {
        return mManager;
      }
      return PermissionManager.getInstance();
    }
    

    当然,严格来说,您的 PermissionManager 应该实现某种 IPermissionManager 接口,那是您的其他类应该引用的,以便在测试期间可以更轻松地替换虚拟实现。

    【讨论】:

    • 通过接口注入这一点很重要。在测试中模拟 PermissionManager 对象的能力使 DI 成为更可测试代码的强大方法。
    • 如果 PermissionManager 本身有依赖项怎么办。有没有比 PermissionManager.getInstance(Foo) 更奇怪的方法? ?
    【解决方案2】:

    您确实可以从注入 PermissionManager 开始。这将使您的课程更具可测试性。

    如果这会给该类的用户带来问题,您可以让他们使用工厂方法或抽象工厂。或者,您可以添加一个无参数构造函数,供他们调用,注入 PermissionManager,而您的测试使用另一个构造函数,您可以使用该构造函数来模拟 PermissionManager。

    更多地解耦你的类可以让你的类更灵活,但也会让它们更难使用。这取决于你需要什么。如果您只有一个 PermissionManager 并且测试使用它的类没有问题,那么就没有理由使用 DI。如果您希望人们能够添加他们自己的 PermissionManager 实现,那么 DI 就是要走的路。

    【讨论】:

      【解决方案3】:

      如果你订阅了依赖注入的方式,任何需要你的PermissionManager的类都应该把它作为一个对象实例注入。控制其实例化(以强制执行单例性质)的机制在更高级别上工作。如果你使用像 Guice 这样的依赖注入框架,它可以完成强制工作。如果您手动进行对象连接,则依赖注入倾向于将执行实例化(新操作员工作)的代码与您的业务逻辑分开。

      不过,不管怎样,经典的“capital-S”单例通常被视为依赖注入上下文中的反模式。

      这些帖子过去对我很有洞察力:

      【讨论】:

        【解决方案4】:

        那么我真的应该要求 API 用户在类的构造函数中传入 PermissionManager 实例吗?即使我只希望我的应用程序存在一个 PermissionManager 实例?

        是的,这就是你需要做的。依赖项是单例/每个请求/每个线程还是工厂方法是您的容器和配置的责任。在 .net 世界中,我们最好依赖 IPermissionsManager 接口来进一步减少耦合,我认为这也是 Java 中的最佳实践。

        【讨论】:

          【解决方案5】:

          单例模式本身也不错,难看的是它的常用方式,因为只需要某个类的单个实例,我认为这是一个很大的错误。

          在这种情况下,我会将 PermissionManager 设为静态类,除非出于任何原因您需要将其设为可实例化类型。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-10-09
            • 2015-11-21
            • 1970-01-01
            • 2014-05-27
            • 1970-01-01
            • 2019-05-06
            • 2020-03-25
            • 1970-01-01
            相关资源
            最近更新 更多