【问题标题】:Singleton better than static?单例比静态好?
【发布时间】:2011-10-25 03:44:13
【问题描述】:

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

我有几个选择。

我可以将此播放器对象传递给所有需要它的东西,这很麻烦。 (依赖注入我觉得叫)

只需将其设为公开静态即可。

使其成为单例。

各有什么优缺点?

【问题讨论】:

  • 请参阅this question 以获得一些可能的指导。
  • 单例几乎永远不是答案。如果您想稍后制作多人游戏怎么办?你把自己画到了角落里。
  • @MattBall,我不太同意。 Singleton 毕竟是一个工厂,它可以很容易地增强以支持 getIntance() 中的 arg,比如 player-id。
  • 所有事情都不需要 DI。并非每个人都将 DI 用于所有事情。这不是很酷的帮助。
  • 单身是新的黑色:)

标签: java singleton global


【解决方案1】:

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

查看我对单身人士here 的看法。简短的总结:它们的典型误用(避免“繁琐”的设置器)违反了面向对象并降低了设计质量。

静态变量和Monostate(非静态getter,静态数据,构造函数是“工厂”)是从同一块布料上剪下来的。避开他们。考虑一下是否将所有内容都设为静态:玩家、地图、相机、敌人等。您将避免使用许多“笨重”的设置器。但它是OO吗?完成游戏后,您是否可以在其他游戏中重复使用您的寻路算法、AI 算法等,或者它们是否有太多特定于您当前游戏的全局变量(Singletons 等)被永久烧毁?

【讨论】:

  • 好的,所以传递它。如果十个班级需要它怎么办?我有一个世界,它有一个地图数组,而那个地图数组有瓦片数组。每个人都必须让玩家传递给它。那没问题?我看到的问题是它适用于游戏,但不适用于我的地图编辑器。
  • @user697111 是的,没关系。我建议使用 Guice 等依赖注入框架:code.google.com/p/google-guice
  • @user697111。我同意保罗。通过将一个类传递给另一个类来“连接”类是要走的路。我喜欢使用 Spring 进行布线,但正如 Paolo 所说,Guice 是一种选择,如果您对任何一种都不满意,只需从您的 main() 或其他任何可行的方法中调用设置器。
  • 关于你的地图编辑器——如果它不能工作,因为它与游戏共享相同的代码,但传递一个玩家对象没有意义,那么设计就不太正确。尝试将关心玩家的代码与不关心玩家的代码分开。这可能表明您的地图不应该了解玩家 - 也许它应该是相反的,或者其他什么......
【解决方案2】:

所以,你的选择是:

只需将其设为公开静态即可。 使其成为单例。

两者都会有效地将它变成一个全局变量。我不是全局变量的忠实拥护者:它们使测试和调试变得更加困难。

优点:更容易访问

缺点:非常高的耦合度(如果你想让它成为一个两人游戏怎么办?);增加了测试的复杂性;在一个地方更改播放器可能会对其他地方产生意想不到的后果。

我可以将此播放器对象传递给所有需要它的东西,这很麻烦。 (依赖注入我觉得叫)

优点: 耦合度较低;便于测试;您可以将播放器的副本传递给其他类并减少副作用的机会。

缺点:必须通过 Player 引用使 API 有点复杂,但可以通过使用依赖注入框架(例如 Guice)来缓解其中的一部分。

【讨论】:

    【解决方案3】:

    除了 Java Setter and Getter 提供的优势之外,我真的想不出单例模式 (public static Type getInstance()) 会为您提供超过公共变量 (public static Type var) 的任何新优势。

    但总的来说,控制对成员变量的访问(尤其是从外部将其作为成员变量的类)总是更好(从未来的 pov 开始),所以我推荐private static带有公共吸气剂的变量。介于单例和公共静态变量之间。

    【讨论】:

      【解决方案4】:

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

      另一点是“概念上”玩家是一个对象而不是一个类。

      【讨论】:

      • 其实传统的Singleton是无法扩展的。
      • 不要以为你明白我的意思。假设 Player 是一个抽象类,那么您可以在运行时使用 Player 单例。您可以在启动程序之前使用简单的配置更改来决定您想要实例化为单例的 Player 的实际具体类。
      【解决方案5】:

      我会避免让它成为静态的。您希望您的代码可重用,并且播放器当然是一个对象,可能需要在备用项目中使用多个实例。

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

      另一种选择是在播放器类中简单地公开可共享属性,尽管我更喜欢获取/编辑方法选项。

      【讨论】:

        【解决方案6】:

        单例可以实现可用于引用单例的接口。也就是说,您不需要在整个代码中对单例进行硬编码引用。如果您需要不同的实例进行测试,这会使单元测试更容易。

        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);
        

        【讨论】:

          猜你喜欢
          • 2016-12-03
          • 2010-11-16
          • 2023-03-08
          • 2011-09-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-02-19
          相关资源
          最近更新 更多