15

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

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

     PermissionManager.getInstance().isReadPermissionEnabled(this)

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

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

还是我要解决这一切都错了,应该有一个非公共构造函数和一个工厂在某个地方为我传递 PermissionManager 的实例?


附加信息请注意,当我说“依赖注入”时,我指的是 DI模式……我没有使用任何 DI 框架,例如 Guice 或 Spring。(...然而)

4

5 回答 5

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 接口,就是您的其他类应该引用的,以便在测试期间可以更轻松地替换虚拟实现。

于 2008-10-29T14:41:58.797 回答
2

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

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

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

于 2008-10-29T14:44:31.147 回答
2

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

然而,无论哪种方式,经典的“capital-S” Singleton 通常被视为依赖注入上下文中的反模式。

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

于 2008-10-29T14:49:30.997 回答
1

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

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

于 2008-10-29T15:00:56.667 回答
0

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

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

于 2008-10-29T14:22:41.783 回答