使用单例的好例子



最近我读了很多关于Singleton模式。就我而言,单例对象应该只在没有必要拥有多个对象,并且需要从整个程序访问它们时才使用。我的问题很简单,只是出于教育目的。当制作桌面游戏模拟器,如大富翁卡坦它是正确的创建骰子(投掷棋盘游戏的骰子)作为一个单一的类?

这里要问的问题是您从Singleton行为中获得了什么值。您将其描述为"只有在没有必要拥有多个对象,并且需要从整个程序中访问它们的情况下"。这里的细微差别在于,即使拥有多个实例没有用处,您也可以选择反对Singleton:如果没有任何明显的缺点,允许创建多个实例可能是好的。

请记住,如果你很好地实现了Singleton,那么该对象在应用程序的整个生命周期内都不能被销毁,并且你需要同步对象的创建,这样多个线程就不能创建多个实例。对于重对象,或具有重依赖性的对象,这实际上可能会导致更大的长期内存使用,因为你不能在应用程序的整个生命周期中销毁或垃圾收集对象。这在哪些对象可以是Singleton和哪些对象应该是Singleton之间留下了一个差距。这需要你的判断。

需要考虑的因素:

  1. 如果创建了多个应用程序,您的应用程序是否正确?对于像DatabaseService或StorageService这样的东西,答案可能是"不",在这种情况下,单例行为是绝对需要的。

  2. 如果创建多个应用程序,您的应用程序是否具有良好的性能?对于像WebRequestService这样的东西,使用一个对象队列或管理请求可能会有一些额外的价值,这可能是使其成为Singleton的一个很好的动机。

  3. 如果你的对象不是单例的,创建它是昂贵的,或者它会经常被创建,想要重用这个对象吗?在这种情况下,您必须权衡创建开销与Singleton开销之间的关系。想象一个Dictionary或SpellChecker,即使您创建了一个新的Dictionary或SpellChecker,其结果也是正确的,但是您希望尽量减少必须从磁盘读取Dictionary文件的次数。有时候有比Singleton更多的选择,比如Dagger的@Reusable作用域,它会以更少的成本给你带来一些好处。

为您的骰子类:

  • 如果一个骰子类在对象创建时只代表一个骰子,显然它不能是单例的,因为你只能掷一次骰子,它总是返回相同的值。你可能不希望这样。
  • 如果你的Dice类代表一个纯(伪)随机数生成器,它通常不必是Singleton的:创建它可能很便宜,并且按顺序请求相同的对象可能没有优势。你可以合理地使它"可重复使用"。或者将Dice对象保存在一个池中,以避免重新创建它们,但如果由我编写代码,我认为这样做不太值得。
  • 如果你希望你的游戏是可重复的,例如在集成测试中有可预测的游戏,那么将Dice对象设置为Singleton可能是有意义的:在这种情况下,你可能希望单个对象可以以相同的顺序调用并接收相同的随机种子结果。
  • 如果你正在调用一个随机数服务,如https://www.random.org,它可能是重要的使对象单例,以便它可以批处理,缓存和重用这些请求。

为"Dice"我将使其"无作用域",每次都创建一个新实例,并允许在测试中替换它。相反,你的Board、Game或GameState对象在整个应用程序中是单例的,这可能是有意义的。

最新更新