Dependency injection 实用单身人士及;依赖注入问题

Dependency injection 实用单身人士及;依赖注入问题,dependency-injection,singleton,Dependency Injection,Singleton,假设我有一个名为PermissionManager的类,它对于我的系统应该只存在一次,基本上实现了管理应用程序中各种操作的各种权限的功能。现在,我的应用程序中有一个类,它需要能够检查其中一个方法中的某个权限。此类的构造函数当前是公共的,即由API用户使用 直到几周前,我还只是让我的类在某个地方调用以下伪代码: PermissionManager.getInstance().isReadPermissionEnabled(this) 但是因为我注意到这里的每个人都讨厌单例+这种耦合,我

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

直到几周前,我还只是让我的类在某个地方调用以下伪代码:

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

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

还是我的做法完全错了,应该在某个地方有一个非公共的构造函数和一个工厂,让我通过PermissionManager的实例



其他信息请注意,当我说“依赖注入”时,我指的是DI…我没有使用任何像Guice或Spring这样的DI框架。(…然而)

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


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

如果您使用的是依赖项注入框架,然后,处理此问题的常用方法是在构造函数中传入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开始。这将使您的类更易于测试

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


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

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

然而,不管怎样,在依赖注入的背景下,经典的“capital-S”单例通常被视为一种反模式

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

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


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

关于通过接口进行注入的一点很重要。在测试中模拟PermissionManager对象的能力使DI成为更可测试代码的强大方法。如果PermissionManager本身具有依赖性该怎么办。有没有比PermissionManager.getInstance(Foo)更奇怪的方法?