我已经阅读了很多关于使用静态类的不同缺陷,所以我从来没有真正打扰过使用静态类。
但是我可以看到创建一个包含所有样式,图像,字体等的静态类的主要优点。方便参考。这会对性能或糟糕的设计造成不利影响吗?
为了帮助解释我的意思,我有一些代码,这是来自静态类:
button2Style = new TextButtonStyle();
button2Style.up = new NinePatchDrawable(buttonAtlas.createPatch("button2"));
button2Style.down = new NinePatchDrawable(buttonAtlas.createPatch("button2")).tint(Color.GRAY);
button2Style.font = font_rr_65;
button2Style.fontColor = Colors.PURPLE;
然后我想在另一个类中引用它:
TextButton txtB = new TextButton("Hello", StaticClass.button2Style);
答案 0 :(得分:3)
尽管通常使用静态通常很好地表明面向对象设计不良,但请注意: 对资产进行静态引用尤其糟糕,因为静态的生命周期可能与创建资源的上下文的生命周期不同。
让我们以@BasimKhajwal的答案为例:
class Assets { /*...*/
private static final AssetManager assetManager = new AssetManager();
此代码new AssetManager()
何时实际执行?初始化VM后,它实际上是直接执行的。这可能是在实际创建应用程序窗口之前,甚至在加载所有本机库并创建OpenGL上下文之前。
public static void loadAll() { /* load things */ }
这段代码什么时候执行?你可能在ApplicationListener的create
方法中调用它。因此,的OpenGL上下文,即应用程序的特定实例在此时创建并处于活动状态,并在该应用程序的呈现线程上调用。
现在考虑在Android上运行的以下情况:
您的VM静态资产已加载到该应用程序上下文中,您可以在该上下文中使用它们。然后,您会收到一个电话,导致您的应用程序在后台推送。过了一会儿,Android决定它需要释放内存并关闭你的应用程序,包括它的OpenGL上下文等。
完成通话后,您将返回游戏。因此,Android会启动游戏的新实例。为此,它重新使用应用程序的前一个实例的相同VM,包括相同的AssetManager和它已加载的所有资产。您的应用程序会创建一个新的OpenGL上下文等,但资产仍然指向旧的无效上下文。从而使您的资产无效并导致不良行为。
当然,在正常测试期间,您不会轻易找到此类问题。并且无法保证Android何时或何时关闭您的应用程序并回收VM。但是如果你尝试一下,你应该可以重现上述步骤,这样你就可以自己看看你的资源是如何变得无效的(例如纹理变黑)。您的应用程序的多个实例在同一个VM中运行(例如,从Play商店和启动屏幕启动时)甚至可以said。
你可以解决这个问题。例如,libGDX在内部通过为每个应用程序实例保留Map
所有资源来实现这一点(here是一个例子,如果你想看看它是如何工作的)。但是,由于您很可能没有正确实施Object Oriented Design,所以最好重新考虑您的方法。
如果你仍然决定使用static
,那么请确保完全理解他们的生命周期(不要假设他们不会比你的应用程序的特定实例更长久或独有)。
请注意,这可能是最常犯的错误之一(使用“虚拟”像素旁边)。如果你搜索一下,你会发现一些使用静态问题的人的例子,特别是在Android上。但请记住,虽然这在Android上展示最多,但它是一个设计问题。这并不意味着当您不定位Android时它不适用于您。完全可以设计您的应用程序,以便您不需要任何静态资源(或任何重要的静态变量)。
答案 1 :(得分:1)
编辑 - 结果我错了:(,请参阅Xoppa的回答
就个人而言,我发现使用静态类对libGDX和游戏开发特别有用。
如果您阅读this等文章,人们会对静态变量的使用感到不满。重点在于变量,因为通常具有变化的全局状态可能更难以调试和推理。当变量是静态的时,代码的任何部分都可能导致这些更改导致错误/崩溃,导致程序完全不同的部分。
对于您的资产,它们将在游戏的大部分时间内保持不变,这意味着它们不会干扰您的调试。此外,您的资产可能必须在整个游戏期间加载,因为大多数游戏在启动时加载所有资产。保持静态将使内存使用量几乎没有差异。
对于我的游戏,我通常倾向于使用一个名为Assets
的静态类,它使用内置的AssetManager
类。然后,我可以使用存储为常量的文件路径轻松引用任何资产。通过这样做,您可以确保其他类无法编辑静态纹理,您可以轻松地遍历纹理。
示例:
class Assets {
/* Use built-in libGDX asset manager to load/store all assets */
private static final AssetManager assetManager = new AssetManager();
/* Use private constructor to prevent instantiation */
private Assets () { }
/*
Public constants to be referenced by other classes
*/
public static final String BAD_GUY = "images/bad_guy.png";
public static final String GOOD_GUY = "images/good_guy.png";
/*
Private arrays of all textures/sounds/fonts for loading
*/
private static final String[] TEXTURES = {
BAD_GUY,
GOOD_GUY;
};
/* Load method for all assets called by your main game handler at start */
public static void loadAll() {
//Setup the load queue for all textures
for (String texture: TEXTURES)
assetManager.load(texture, Texture.class);
//Load everything
assetManager.finishLoading();
/* Do all the post processing here */
}
/* Access method for textures the parameter name is from above e.g. Assets.BAD_GUY */
public Texture getTexture(String name) {
return assetManager.get(name, Texture.class);
}
}
如果你想看一个真实的例子,你可以看到我自己的游戏文件: https://github.com/basimkhajwal/LSD/blob/master/core/src/net/net63/codearcade/LSD/managers/Assets.java
请注意,它有点复杂,因为它包含声音,动画和一些特定于游戏的代码,但主要原则是相同的。