单例模式比静态方法更好吗?

4
我在我的游戏中有一个玩家类。从逻辑上讲,只需要一个玩家对象(单人游戏),但很多不同的类需要访问玩家对象(例如,地图需要知道玩家在哪里,摄像机和敌人需要与玩家交互等)。
我有几个选择:
1. 将这个玩家对象传递给所有需要它的东西,这很麻烦。(我想这被称为依赖注入) 2. 只需将其设置为公共静态变量。 3. 将其设置为单例模式。
每种方法的优缺点是什么?

1
请参考这个问题以获取一些可能的指导。 - brc
4
单例模式几乎从来不是最好的选择。如果你日后想让游戏支持多人模式,那么你就会陷入困境。 - Matt Ball
2
@MattBall,我不太同意。Singleton毕竟是一个工厂,可以很容易地增强支持getIntance()中的参数,比如player-id。 - Kashyap
2
你不需要在所有情况下都使用依赖注入(DI)。并非每个人都会在所有情况下使用DI。这不是什么神奇的东西。 - DarthVader
3
单例模式是新宠。 - Shahzeb
显示剩余10条评论
6个回答

4

我不会在这里使用Singleton或静态变量,而是通过setter将Player实例传递给需要它的类。如果您只需要一个玩家实例-只需调用new Player()一次即可:-)

请查看我对单例模式此处的看法。简短总结:它们的典型误用(避免“繁琐”的setter)违反了面向对象编程原则并降低了设计质量。

静态变量与Singletons同出一辙,还有Monostate(非静态getter,静态数据,构造函数是“工厂”)。请避免使用它们。考虑一下,如果您将所有内容都设为静态:玩家、地图、摄像机、敌人等,那么您将避免很多“繁琐”的setter。但这是面向对象编程吗?当您完成游戏时,您是否可以在另一个游戏中重复使用您的路径规划算法、AI算法等,或者它们中有太多全局变量(Singletons等),这些变量永远特定于您当前的游戏?


好的,那就传递它。但是如果有十个类需要它呢?我有一个世界,其中有一个地图数组,而该地图数组又有瓦片数组。每个瓦片数组都必须得到传递给它的玩家。这样可以吗?我看到的问题是它对于游戏来说是可行的,但对于我的地图编辑器来说却不可行。 - user697111
@user697111 嗯,没问题。我建议使用依赖注入框架,比如Guice:http://code.google.com/p/google-guice/ - PaoloVictor
@user697111。我同意Paolo的观点。通过将一个类传递到另一个类的setter中来“连接”它们是可行的方法。我更喜欢使用Spring来进行连接,但正如Paolo所说,Guice也是一种选择,如果你对这两个框架都不熟悉,直接从你的main()或其他地方调用setter也可以。 - SingleShot
关于你的地图编辑器 - 如果它不能工作,因为它与游戏共享相同的代码,但传递玩家对象没有意义,那么设计就不太正确。尝试将关心玩家的代码与不关心玩家的代码分开。这可能表明你的地图不应该知道玩家 - 也许应该反过来,或者是其他什么... - SingleShot

1

所以,你的选择有:

将其设置为public static。 将其设置为单例。

这两种方法都可以有效地将其转换为全局变量。我不是全局变量的忠实粉丝:它们使测试和调试变得更加困难。

优点:访问更容易

缺点:耦合度非常高(如果您想将其变成双人游戏怎么办?);增加了测试的复杂性;在一个地方更改玩家可能会对其他地方产生意想不到的后果。

我可以将此玩家对象传递给需要它的所有内容,但这很麻烦。(我认为这被称为依赖注入)

优点:耦合度较低;便于测试;您可以将玩家的副本传递给其他类,并减少副作用的可能性。

缺点:必须传递Player引用,这使API变得有点复杂,但使用依赖注入框架(例如Guice)可以缓解部分问题。


0

单例模式可以实现接口,这些接口可用于引用单例。也就是说,您不需要在代码中到处使用硬编码的单例引用。这使得单元测试更加容易,如果您想要一个不同的实例进行测试。

public interface Printer {
   public void print(String line);
}

public enum ConsolePrinter implements Printer {
   INSTANCE;
   public void print(String line) {
       System.out.println(line);
   }
}

// to print to the screen
Printer printer = ConsolePrinter.INSTANCE;

// for testing purposes.
Printer printer = createMock(Printer.class);

0

除了Java Setter和Getter提供的优势外,我真的想不出单例模式(public static Type getInstance())相对于公共变量(public static Type var)会给你带来什么新的好处。

但总的来说,控制成员变量的访问(特别是从具有此成员变量的类外部访问)始终更好(从未来的角度考虑),因此我建议使用具有公共getter的private static变量。这介于单例和公共静态变量之间。


0
使用单例可以扩展基类并提供 Player 的替代实现,而使用静态方法则不具备这种灵活性。
另一个要点是,“从概念上讲”,Player 是一个对象而不是一个类。

实际上,传统的单例模式是无法被扩展的。 - SingleShot
别以为你懂我的意思。假设Player是一个抽象类,那么你可以在运行时创建一个Player的单例。在启动程序之前,你可以通过简单的配置更改来决定要实例化为单例的Player的具体子类。 - RAY

0

我会避免将其设置为静态。你希望你的代码可重用,而玩家肯定是一个可能需要在另一个项目中拥有多个实例的对象。

我会创建简单的getAttribute()和editAttribute()方法,以返回或编辑我需要的属性。

另一个选择是在player类中直接将可共享的属性公开,尽管我更喜欢使用get/edit方法选项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接