Java 单例优于静态?

Java 单例优于静态?,java,singleton,global,Java,Singleton,Global,所以我在游戏中有一个玩家类。从逻辑上讲,只需要一个player对象(单个player),但是许多不同的类需要访问player对象。(即,地图需要知道玩家是谁,摄像机和敌人需要与玩家互动,等等) 我有几个选择 或者我可以将这个玩家对象传递给任何需要它的东西,这很麻烦。(我想它叫依赖注入) 把它公之于众 让它成为一个单身汉 每种方法的优点/缺点是什么?除了提供的优点之外,我真的想不出单例模式(公共静态类型getInstance())比公共变量(公共静态类型var)能给您带来什么新的优点 但一般来说(

所以我在游戏中有一个玩家类。从逻辑上讲,只需要一个player对象(单个player),但是许多不同的类需要访问player对象。(即,地图需要知道玩家是谁,摄像机和敌人需要与玩家互动,等等)

我有几个选择

或者我可以将这个玩家对象传递给任何需要它的东西,这很麻烦。(我想它叫依赖注入)

把它公之于众

让它成为一个单身汉


每种方法的优点/缺点是什么?

除了提供的优点之外,我真的想不出单例模式(
公共静态类型getInstance()
)比公共变量(
公共静态类型var
)能给您带来什么新的优点


但一般来说(从未来的pov)控制对成员变量的访问总是更好的(特别是从外部访问将此作为成员变量的类),因此我建议使用带有公共getter的
私有静态
变量。它介于单例和公共静态变量之间。

使用单例可以扩展基类并提供播放器的替代实现,使用静态方法现在允许这种灵活性


另一点是“概念上”一个播放器是一个对象而不是一个类。

我不会在这里使用单一变量或静态变量,而是通过setter将一个
player
实例传递给需要它的类。如果您只需要一个播放器实例-只需调用
newplayer()
一次:-)

看看我对单身汉的看法。简短总结:它们的典型误用(避免“麻烦”的设置器)违反了OO并降低了设计质量


静态变量与单例从同一块布上切割而成,还有(非静态getter、静态数据、构造函数是“工厂”)。避开它们。如果你把所有的东西都做成静态的:玩家、地图、相机、敌人等等,你会避免很多“麻烦”的设定者。但是它是OO吗?当你完成游戏后,你能在另一个游戏上重复使用你的寻路算法、人工智能算法等吗?或者它们是否有太多特定于你当前游戏的全局变量(Singleton等)永久地被烧掉了?

我会避免使其成为静态的。您希望您的代码是可重用的,而且播放器肯定是一个对象,在一个备用项目中可能需要几个实例

我将创建简单的getAttribute()、editAttribute方法,用于返回或编辑所需的属性


另一个选项是简单地在player类中公开可共享属性,尽管我更喜欢get/edit methods选项。

单例可以实现可用于引用单例的接口。i、 e.在整个代码中,您不需要硬编码的对单例的引用。如果您希望使用不同的实例进行测试,这将使单元测试更容易

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);
因此,您的选择是:

把它公之于众。 让它成为一个单身汉

两者都将有效地将其转化为一个全局变量。我不是globals的超级粉丝:他们让一切都变得更难测试和调试

优点:更容易访问

缺点:非常高的耦合性(如果你想让它成为一个两人游戏怎么办?);增加了测试的复杂性;在一个地方更换玩家可能会给其他地方带来意想不到的后果

我可以将这个玩家对象传递给任何需要它的东西,这很麻烦。(我想它叫依赖注入)

优点:下联轴器;方便测试;你可以把玩家的副本传给其他职业,减少副作用的机会


缺点:必须通过
Player
引用使得API有点复杂,但是使用依赖注入框架可以减轻部分问题,例如。

有关一些可能的指导,请参阅。Singleton几乎不是解决办法。如果你以后想让你的游戏成为多人游戏呢?“你把自己画到了一个角落里。”马特波尔,我不太同意。Singleton毕竟是一个工厂,它可以很容易地增强,以支持
getIntance()
中的arg,就像player-id一样。您不需要DI来处理所有事情。并不是每个人都使用DI做任何事情。这不是很酷的帮助。单身是新的黑人:)事实上,传统的单身是不能扩展的。别以为你明白我的意思。假设Player是一个抽象类,那么您可以在运行时创建一个Player单例。在启动程序之前,您可以使用一个简单的配置更改来决定要实例化为singleton.ok的播放器的实际具体类,所以请传递它。如果有十个班需要它呢?我有一个世界,有一个地图阵列,地图阵列有瓷砖阵列。每个人都必须让球员通过。可以吗?我看到的问题是,它适用于游戏,但不适用于我的地图编辑器。@user697111是的,没关系。我建议使用依赖注入框架,比如Guice:@user697111。我同意保罗的观点。通过将一个类传递到另一个类上的setter来将类“连接”在一起是一种方法。我倾向于使用Spring进行布线,但正如Paolo所说,Guice是一种选择,如果您对其中任何一种都不满意,只需从
main()
或其他任何方式调用setter即可。关于地图编辑器,如果它无法工作,因为它与游戏共享相同的代码,但传入玩家对象没有意义,那么设计就不对了。试着把关心玩家的代码和不关心玩家的代码分开。这可能是一个指标,你的地图不应该知道球员-也许它应该是另一种方式,或其他东西。。。