实用的Singleton&依赖注入问题

时间:2008-10-29 14:17:20

标签: dependency-injection singleton

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

直到几个星期前,我本来只是让我的班级在某处调用以下伪代码:

     PermissionManager.getInstance().isReadPermissionEnabled(this)

但是因为我注意到每个人都讨厌单身人士+这种耦合,我想知道更好的解决方案会是什么,因为我读过针对单身人士的论证似乎有意义(不可测试,高耦合等)

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

或者我是否认为这一切都错了,应该有一个非公共构造函数和一个工厂,它会在我的PermissionManager实例中传递给我?


其他信息请注意,当我说“依赖注入”时,我在谈论DI Pattern ...我没有使用像Guice或Spring这样的任何DI框架。 (...还)

5 个答案:

答案 0 :(得分:3)

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

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

对于类的客户端来说,知道在哪里找到正确的PermissionManager实例并将其传入(或者甚至关心您的类使用PermissionManager这一事实),这是正确的,这是正确的。

我见过的一个妥协解决方案是为您的类提供PermissionManager类型的属性。如果已设置属性(例如,在单元测试中),则使用该实例,否则使用单例。类似的东西:

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

当然,严格来说,你的PermissionManager应该实现某种IPermissionManager接口,那是你的其他类应该引用的东西,这样在测试过程中可以更容易地替换虚拟实现。

答案 1 :(得分:2)

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

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

将您的课程更加分离会使您的课程更加灵活,但也会使课程难以使用。这取决于您需要的情况。如果您只有一个PermissionManager并且测试使用它的类没有问题,那么就没有理由使用DI。如果您希望人们能够添加自己的PermissionManager实现,那么DI就是您的选择。

答案 2 :(得分:2)

如果您正在订阅依赖注入的处理方式,那么无论您需要什么类,PermissionManager都应该将它作为对象实例注入。控制其实例化(以强制执行单例性质)的机制在更高级别上工作。如果您使用像Guice这样的依赖注入框架,它可以执行强制执行工作。如果您正在手动进行对象连接,依赖注入有利于将实例化(新操作员工作)的代码分组,远离业务逻辑。

无论哪种方式,经典的“资本-S”Singleton在依赖注入的背景下通常被视为一种反模式。

这些帖子过去对我很有见地:

答案 3 :(得分:1)

  

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

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

答案 4 :(得分:0)

单身模式本身并不坏,它的丑陋之处在于它常用的方式,因为只需要某个类的单个实例,我认为这是一个很大的错误。

在这种情况下,我会将PermissionManager设为静态类,除非出于任何原因需要它是一个可实现的类型。