Singletons / Globals的替代品


16

我已经听过无数次有关Singletons /全局变量的陷阱的信息,而且我理解为什么它们如此频繁地皱眉。

我不明白什么是优雅的,非混乱的选择。似乎使用Singletons / globals的替代方法总是涉及通过引擎对象向下传递一百万个级别的对象,直到它们到达需要它们的对象为止。

例如,在我的游戏中,我会在游戏启动时预加载一些资产。直到玩家在主菜单中导航并进入游戏时,这些资产才被使用。我是否应该将这些数据从Game对象传递到ScreenManager对象(尽管实际上只有一个Screen关心该数据),然后再传递给适当的Screen对象以及其他任何地方?

似乎我正在交换全局状态数据以进行混乱的依赖注入,将数据传递给什至不关心数据的对象,除非是将其传递给子对象。

这是Singleton会是一件好事的情况,还是我缺少一些优雅的解决方案?

Answers:


16

不要混淆单例和全局变量。尽管通常需要某种全局变量,但是单例不仅是全局变量的替代,而且主要 是解决C ++和FQA静态初始化顺序问题的一种方法。(在其他语言中,这是解决不同语言缺陷(例如缺少全局变量和裸函数)的一种方法。)

如果您可以只使用全局指针而不是单例指针,并确保在任何需要它之前已对其进行了初始化(手动),则可以避免函数调用和分支开销,获取对象的the脚语法,并且实际上可以使当您需要测试或因为您的设计更改时,该类的第二个实例。

对于您需要的几个全局变量(常见示例是音频输出,打开的窗口列表,键盘处理程序等),我建议使用服务定位器模式。它使您可以轻松地用不同的实现方式替换事物(例如,真实音频设备与空音频设备),并将所有全局变量收集到一个结构中,以避免污染您的命名空间。


+1。好的答案,并感谢您提供的服务定位器模式链接。这是一个非常有趣的阅读。
bummzack,2010年

1

如果您没有/无法神奇地“了解”某些数据,则需要以某种方式传递它。但是,这并不意味着它必须仅通过参数传递。

在您的示例案例中,您是否可以使用某种“ AssetManager”来加载和存储资产,然后仅需要给ScreenManager一个引用(可能是在创建时)?从这种意义上讲,您正在传递对包装在另一个对象中的资产的引用,并且可以在初始化时将其传递一次,而不是在需要时将其传递给叶子函数。

现在,恕我直言,AssetManager是您只想要的一种,也可能是单例。只要您了解陷阱,并专门编写代码来避免陷阱(假设将同时从多个线程访问单例,并在需要执行任何阻止操作时使用fork刺入自己),然后淘汰自己。


1

我认为Jason D绝对正确-这就是我的处理方式:

游戏具有AssetManager的实例,该对象可以从名称中获取任何资产。

在游戏中:

assetManager = new AssetManager();
screenManager = new ScreenManager();
screenManager.assetManager = assetManager;

在ScreenManager中:

screen = new Screen();
screen.assetManager = assetManager;

在屏幕上:

myAsset = assetManager.getBitmp("lava.png");

现在,所有屏幕都可以访问所需的任何资产。这比使用globals或Singletons更为复杂或疯狂,并且您可以选择在同一应用程序中运行2个Game实例而不会发生冲突。我曾经不得不制作一个由8个迷你游戏组成的游戏,它们都共享相同的基类/框架。我必须重构所有的全局变量/单例才能使用这种引用传递样式,而且我从不回头。应该是全局变量的唯一事物是只能物理存在一次的事物,例如音频,网络,I / O等。


0

您可以使用Factory模式替换Singleton。然后,工厂类可以控制可以创建的实例数量,以后发现需要多个实例时,可以轻松更改这些实例AssetManager如本文所述

它为您提供了Singleton的所有灵活性,没有那么多问题。


另一个相当有限的可能性是使类静态化(我认为这对于AssetManager不可行,并且仅在完全具有静态类的语言中才可行)。但这仅在不需要继承/多态的情况下有效。这是一个非常不灵活的解决方案:

静态方法与花岗岩一样灵活。每次使用它时,您都将部分程序转换为具体的。只要确保您的脚变硬,就不会把脚塞在那里。总有一天,您会惊奇地发现,确实需要该dang PrintSpooler类的另一种实现,它应该是一个接口,一个工厂和一组实现类。天哪!

这是关于静态方法的,但是它也可以应用于静态类。


“将类设为静态”是什么意思?C ++没有静态类。您是说只有静态方法吗?那为什么要麻烦一个类而不是一个名字空间呢?

1
@Joe:嗯,据我所知,问题并不集中在C ++上。在C#或Java中,您可以创建静态类,而我指的是这些类。而且,正如我所说,静态类在大多数情况下并不是最佳解决方案,但是在极少数情况下,它可以像全局类一样工作。
迈克尔·克莱门特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.