XNA游戏服务和荣耀的全局变量之间有什么区别?


10

Microsoft.Xna.Framework.Game班有一个服务属性,它允许程序员通过提供一流的类型和类Add方法的一个实例,一个服务添加到他们的游戏。

现在,不必将传递AudioComponent给需要它的所有类和方法,而只需传递Game实例并查找服务即可。(服务定位器

现在,由于游戏具有许多服务(GraphicsDevice,SceneGraph,AudioComponent,EffectsManager等),因此您现在基本上将Game传递给了一切。

那么,为什么不只将这些实例设置为Singleton呢?好吧,因为单例很糟糕,因为它们具有全局状态,可以防止测试,并使您的设计更加脆弱。同样,服务定位器被视为对许多人的反模式,因为您传递的是服务定位器(游戏),而不是将依赖项传递给对象,该服务定位器将此类与其他服务耦合在一起。

那么,为什么在XNA和游戏开发中推荐“服务”呢?是因为游戏不同于常规程序,并且与它们的组成部分高度交织在一起,并且必须通过类的功能所必需的每个组成部分都非常繁琐吗?游戏服务是游戏设计中必不可少的邪恶吗?是否有其他选择不涉及冗长的参数列表和耦合?


3
“服务定位器同样被认为是许多人的反模式”-[需要引用]。显然,在不需要它们的地方使用它们是不好的-对于所有软件结构都是如此-但我从未听说过称为反模式的服务定位器。(另一方面,我也试图避免程序员花费大量时间讨论最新的模式细节。)

@JoeWreschnig尽管我同意您的意见,但我进行了一些挖掘并发现了这一点:blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
Roy T.

2
是的,但是那个家伙正在卖一本有关使用DI的书;显然他不希望您使用简单的东西,那么您就不会买他的书!(说真的,Fowler最初的DI / SL文章涵盖了这个确切的主题-martinfowler.com/articles/…,而那家伙的博客专家对此没有添加任何讨论。)

我希望我可以将您刚刚从马丁·福勒(Martin Fowler)发布的链接标记为答案(尽管这不是我的问题)
Roy T.

Answers:


6

我不认为每个人都认为服务定位器很糟糕。服务定位器提供了一种将每个人所需的重要功能与实际实现脱钩的方法。服务定位器还提供了一种在运行时交换服务的方法。而且我也不认为您确实需要遍历所有地方的服务定位器。我认为您可以从任何地方访问它。虽然具有全局变量的游戏类需要每帧传递大约一千次(这会导致性能下降)。

为了使服务定位器和具有全局变量的类之间的区别更加清楚,请考虑以下示例:

我正在制作游戏,并使用一个具有全局变量的gameA类对象:内容加载器。现在,在我的下一个游戏中,我不想使用gameA类,而是创建一个新的gameB类。现在,我必须再次将gameA的全局变量添加到gameB中,这是额外的工作。同样,配置我想要的内容加载器的类型也变得困难。假设我有一款既可以在PC上也可以在XBOX上运行的游戏。在PC上,我需要一个内容加载器,它也可以打开“打开文件”对话框。在XBOX上,我绝对不想要这个。通过拥有配置文件,我什至可以在游戏进行到一半时轻松更改内容加载器,甚至可以从gameA类之外的其他位置更改内容加载器,而不必只是将gameA中的全局变量全部公开。

现在考虑我正在使用服务定位器。我可以一直使用它。甚至什么类型的服务都可能始终存在差异。的确,只要它们具有公共接口,我什至不关心它们的实现。(尽管我也可以在类似类的gamA中使用它)。

无论如何,我认为您会发现游戏服务非常方便。我认识的每个人都在使用它。为了帮助您更多。我围绕游戏服务创建了一个小型帮助程序类,以便您可以始终减少很多转换:http : //roy-t.nl/index.php/2010/08/25/xna-accessing-contentmanager随时随地在游戏服务容器中安装图形设备/经常使用它。


谢谢回复。在需要服务的类中,您通常只在需要时向服务请求GameServices类,还是在类中创建成员变量并仅在构造函数中请求服务?我想我要问的是有多慢return (T)Instance.GetService(typeof(T));
约翰·波利克

另外,您如何看待GameServices类的这种实现?我删除了不必要的单例,因为它没有抽象所没有的任何功能。另外,Service在每种方法的结尾,我都删除了多余的后缀。
约翰·波利克

另外,我认为Services AddService方法采用类型的原因是可以指定服务的接口。例如,Services.AddService(typeof(IGraphicsDeviceManager), graphics);。然后,在检索服务时,可以将接口指定为类型并将其强制转换为所选的子类。
约翰·波利克

@JohnPollick,第一个问题。我尝试始终请求它(因为它可能已经更改),但是如果我确实需要每一个框架,我都会创建一个成员变量。除了没有对我的瓶颈之外,我没有太多的性能统计数据。第二,是的,看起来不错:)。第三。这确实是类型参数的原因,但是,您永远不要养成向下转换检索值的习惯,因为这会破坏您将类作为服务交换的能力。如果您不希望使用IGraphicsDeviceManger而是MyGDM,则可以创建一个新接口。
罗伊(Roy T.)

1
哦,我没看到你也被抛弃。当然,如果您先转换:)就能推断出类型。
罗伊(Roy T.)

5

那么,为什么不只将这些实例设置为Singleton呢?好吧,因为单例很糟糕,因为它们具有全局状态,可以防止测试,并使您的设计更加脆弱。

单例通常具有许多有用的特征,并将它们打包到一个糟糕的混合体中,从而为您提供一些您不想要的东西以及您想要的任何东西。(是否具有“按需创建”特性?全局范围?至少执行一个实例?禁止多个实例?)

同样,服务定位器被视为对许多人的反模式,因为您传递的是服务定位器(游戏),而不是将依赖项传递给对象,该服务定位器将此类与其他服务耦合在一起。

您无法避免完全耦合。事物使用其他事物,没有它,您的程序什么也不做。因此,唯一真正的问题是抽象级别是多少。

大多数有关面向对象的文本在“合成”和“聚合”之间进行了区分。重要的是要了解您所拥有的东西与所知道的东西之间的概念差异。不幸的是,大多数现代语言在这里没有明显的区别,对对象的引用可能意味着这两种情况。(具有讽刺意味的是,如果使用各种智能指针类型,C ++会做得更好。)

在其中一个方法可以使组成和聚集在你的代码清楚是通过使用服务聚合对象。服务是提供对您不拥有但可以使用的东西的访问的一种方法。

那么,为什么在XNA和游戏开发中推荐“服务”呢?

我认为游戏开发中不建议普遍使用它们。

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.