单身人士的好例子?

时间:2010-08-17 05:01:56

标签: java static singleton

我有一个应用程序,它有几个用于存储应用程序范围设置的类(资源位置,用户设置等)。现在这些类只是充满了静态字段和方法,但我从来没有实例化它们。

有人建议我让他们成为单身人士,是什么/反对?

10 个答案:

答案 0 :(得分:10)

我认为Singleton模式是最不适用的设计模式。在大约12年的软件开发中,我会说我可能会看到5个合适的例子。

我参与了一个项目,我们有一个系统监控服务,用一个System类建模我们的系统(不要与Java的内置System类混淆),其中包含一个{ {1}}每个都有一个Subsystem列表,依此类推。设计师让Component成为单身人士。我问“为什么这是一个单身人士?”答:“嗯,只有一个系统。” “我知道,但是,你为什么要把它变成一个Singleton?难道你不能只实例化一个普通类的一个实例并将它传递给需要它的类吗?” “在任何地方调用System而不是传递它更容易。” “噢...”

此示例是典型的:单身人士经常被误用作访问类的单个实例的便捷方式,而不是出于技术原因强制执行唯一实例。但这需要付出代价。当一个类依赖于getInstance()时,它将永远绑定到Singleton实现。这使得它不易测试,可重用和可配置。它违反了我遵循的基本规则,并且在某些设计原则文章中可能有一个共同的名称:类不应该知道如何实例化它们的依赖项。为什么?因为它将类硬编码在一起。当类调用构造函数时,它将绑定到实现。 getInstance()也不例外。更好的选择是将接口传递给类,而其他东西可以执行构造函数/ getInstance() /工厂调用。这就是像Spring这样的依赖注入框架的用武之地,尽管它们不是必需的(只是非常好)。

那么什么时候使用Singleton是合适的?在那种罕见的情况下,实例化多个东西会破坏应用程序。我不是在谈论在太阳系应用程序中实例化两个地球 - 这只是一个错误。我的意思是,如果你不止一次调用/分配/实例化它,会有一些底层的硬件或软件资源会炸毁你的应用程序。即使在这种情况下,使用Singleton的类也不应该知道它是Singleton。应该只有一个getInstance()的调用返回一个接口,然后传递给需要它的类的构造函数/ setter。我想另一种说法是你应该使用Singleton来实现“单一性”而不是“全局可访问性”。

顺便说一下,在那个项目中,我提到getInstance()是一个单身人士......好System在整个代码库中都被包含在内,还有其他一些不合适的单身人士。一年后,一些新要求降下来:“我们正在将系统部署到多个站点,并希望系统监控服务能够监控每个实例。”每个实例......嗯...... System.getInstance()都不会削减它: - )

答案 1 :(得分:8)

有效的Java说:

Singletons typically represent some system component that is intrinsically 
unique, such as a video display or file system.

因此,如果您的组件在整个应用程序中保证单个实例并且它具有某种状态 ,那么将其设置为单例是有意义的

在您的情况下,应用程序的设置是单身人士的一个很好的候选人。

另一方面,如果要将某些函数组合在一起,则类只能有静态方法,例如实用程序类,jdk中的示例是java.util.Arrays java.util.Collections。这些有几个相关的方法作用于数组或集合

答案 2 :(得分:1)

Singleton将为您提供一个对象引用,您可以在整个应用程序中使用... 如果你想要对象和/或多态,你将使用单例...

答案 3 :(得分:0)

如果你不需要实例化它们,我就不会看到单身人士的观点。好吧,我应该修改一下 - 如果这些类不能实例化,那么将它们与单例进行比较是没有意义的 - 单例模式将实例化限制为一个对象,并将其与不能实现的对象进行比较被实例化是没有意义的。

我发现我对单例的主要用途通常涉及一个具有静态方法的类,在准备环境之后,可以实例化自己的实例。通过使用私有构造函数并重写Object.clone()来抛出CloneNotSupportedException,没有其他类可以创建它的新实例,或者如果它们传递了它的实例,则它们无法克隆它。

我想我会说,如果您的应用程序设置是从未实例化的类的一部分,则说“它应该/不应该是单例”并不相关。

答案 4 :(得分:0)

我认为你不需要创建单例类。 只需将此类构造函数设为私有。 就像java中的Math类一样。

public final class Math {

    /**
     * Don't let anyone instantiate this class.
     */
    private Math() {}

//static fields and methods

}

答案 5 :(得分:0)

单身人士经常出现在你所描述的情况中 - 你有一些需要住在某个地方的全球数据,而你只需要一个,最好是你要确保你只能拥有一。

您可以做的最简单的事情是静态变量:

public class Globals{
   public static final int ACONSTANT=1;

这很好,并且确保您只有一个,并且没有实例化问题。当然,主要缺点是将数据放入其中通常很不方便。例如,如果您的字符串实际构建了,例如,通过外部资源构建某些内容,它也会遇到加载问题(还有一个问题)如果你的静态final是一个int,比如说依赖它的类将它编译成内联,这意味着重新编译你的常量可能不会替换app中的常量 - 例如,给定public class B{ int i = Globals.ACONSTANT; }改变Globals.ACONSTANT并且仅重新编译Globals将使Bi仍然= 1。)

构建自己的单例是下一个最简单的事情,并且通常很好(尽管查找有关单例加载中固有问题的讨论,例如double-checked locking)。 这些问题是使用Spring,Guice或其他管理资源加载的框架构建许多应用程序的一个重要原因。

所以基本上: 静力学

  • 好:易于编码,代码清晰简单
  • bad:不可配置 - 您必须重新编译才能更改您的值,如果您的全局数据需要复杂的初始化,则可能无法正常工作

Singletons解决了其中一些问题,依赖注入框架修复并简化了单例加载中涉及的一些问题。

答案 6 :(得分:0)

由于你的班级正在举行全局设置,单身人士的专业人士可能是你对单身人士的创作有了更多的控制权。您可以在对象创建期间读取配置文件。

在其他情况下,如果方法是静态的,那么没有像javas Math类那样只有静态成员的好处。

对单身人士更明显的需求是当你将工厂作为单身人士实施时,因为你可以互换这个工厂的不同实现。

答案 7 :(得分:0)

有时人们认为属于Singleton对象的东西实际上可以是一个类的私有成员。其他时候它们应该是唯一的全局变量。取决于设计需要它们。

如果一个 完全 一个对象实例:使用Singleton。通过这个,我的意思是如果有多个对象,程序应该是HALT。一个很好的例子就是你设计的视频游戏只支持渲染到单个输出设备。试图再次打开相同的设备(对你的硬编码感到羞耻!)将被禁止。恕我直言,这样的情况通常意味着你不应该首先使用类。甚至C允许您轻松地封装这样的问题,而不需要制作Singleton类的复杂性,并且仍然保持适用于单例的OO元素。当你遇到像Java / C#这样的语言时,单例模式就是你必须使用的模式,除非纯静态成员可以自己动手。您仍然可以模拟其他方式。

如果仅仅是对象接口的情况,您可能应该考虑更多面向对象。这是另一个例子:让我们说我们的游戏引擎渲染代码需要接口资源和输入管理器,所以它可以做它的工作。你可以制作那些单身,并且像ResourceManager.getInstance()。getResource(name)那样做。或者您可以创建一个具有ResourceManager和InputManager作为私有成员的应用程序类(例如GameEngine)。然后让GameEngine根据需要将这些传递给渲染代码的方法。如r.render(resourcemanager);。

对于单身人士 - 可以从任何地方轻松访问,它就像一个全局变量,但只能有一个副本。

对抗单身人士 - 单身人士的许多用法可以通过将其封装在父对象中并将成员对象传递给另一个成员对象方法来解决。

有时候只使用愚蠢的全局变量是正确的。比如使用GOTO或复合(和/或)条件语句,而不是使用复制和粘贴编写相同的错误处理代码N次。


代码更聪明,而不是更难。

答案 8 :(得分:0)

你应该使用单例进行模块化。

想象一下单身中的以下实体:

Printer prt;
HTTPInfo httpInfo;
PageConfig pgCfg;
ConnectionPool cxPool;

案例1 。 想象一下,如果你不这样做,而是一个类来保存所有静态字段/方法。 那么你将有一个很大的静态池来处理。

案例2
在您当前的情况下,您确实将它们分隔为适当的类,但作为静态引用。然后会有太多的噪音,因为现在每个静态属性都可用。如果您不需要这些信息,特别是当有大量静态信息时,那么您应该限制代码的当前范围以查看不需要的信息。

防止数据混乱有助于维护并确保依赖性受到限制。在我目前的编码范围内以某种方式了解我可以使用或不可用的内容有助于我更有效地编码。

案例3 资源标识。
单身人士可以轻松扩展资源。我们假设您现在有一个要处理的数据库,因此,您将其所有设置作为静态放在MyConnection类中。如果需要连接到多个数据库的时候会怎样?如果您已将连接信息编码为单例,则代码增强将比较简单。

案例4 继承。
Singleton类允许自己扩展。如果您有一类资源,他们可以共享公共代码。假设您有一个BasicPrinter类,它可以实例化为singleton。然后你有一个扩展BasicPrinter的LaserPrinter。

如果您使用过静态方法,那么您的代码将会中断,因为您无法将BasicPrinter.isAlive作为LaserPrinter.isAlive访问。然后,除非您放置冗余代码,否则您的单个代码将无法管理不同类型的打印机。

如果您使用Java进行编码,您仍然可以实例化一个完全静态的内容类,并使用实例引用来访问其静态属性。如果有人应该这样做,为什么不把它变成单身?

当然,扩展单例类除了讨论之外还有其他问题,但有一些简单的方法可以缓解这些问题。

案例5 避免信息哗众取宠。 有很少的信息需要全局可用,如最大和最小的整数。为什么要让Printer.isAlive成为看台?只允许一组非常有限的信息来制作看台。

有一种说法:全球化思考,本地化行动。同样,程序员应该使用单身人士进行全球思考,但要在本地采取行动。

答案 9 :(得分:0)

有效的Java说:

单身人士通常代表一些内在的系统组件 独特的,例如视频显示或文件系统。

因此,如果您的组件在整个应用程序中保证单个实例并且它具有某种状态,那么将其设置为单例是有意义的。

(上面赤裸裸地从naikus借来)

在许多情况下,上述情况可以通过“实用程序类”来处理,其中所有方法都是静态的。但有时单身人士仍然是首选。

在静态实用程序类中提倡Singleton的主要原因是在设置它时涉及不可忽略的成本。例如,如果您的类表示文件系统,则需要进行一些初始化,这可以放在Singleton的构造函数中,但是必须在静态初始化程序中调用静态实用程序类。如果您的应用程序的某些执行可能永远不会访问文件系统,那么使用静态实用程序类,您仍然需要支付初始化它的成本,即使您不需要它。如果您从不需要实例化它,那么使用Singleton就不会调用初始化代码。

说了这么多,Singleton几乎肯定是最被滥用的设计模式。