在我的Java游戏项目中过度使用static?

在我的Java游戏项目中过度使用static?,java,static,Java,Static,我目前正在用Java开发一个小平台,并为此编写了自己的游戏引擎,名为。现在我问自己“我是否过度使用了静力学?” 一方面,它非常方便,因为我不必在每个类中保留对游戏实例的引用,比如地图或玩家。另一方面我已经不得不去掉applet的支持,因为里面有很多静态的东西 所以我的问题是,既然你可能比我更有经验的Java程序员,我应该去掉所有的静态吗?如果是的话,有什么有效的方法可以达到这样的效果: public void draw(Graphics2D) { if (this.game.time() &

我目前正在用Java开发一个小平台,并为此编写了自己的游戏引擎,名为。现在我问自己“我是否过度使用了静力学?”

一方面,它非常方便,因为我不必在每个类中保留对游戏实例的引用,比如地图或玩家。另一方面我已经不得不去掉applet的支持,因为里面有很多静态的东西

所以我的问题是,既然你可能比我更有经验的Java程序员,我应该去掉所有的静态吗?如果是的话,有什么有效的方法可以达到这样的效果:

public void draw(Graphics2D) {
  if (this.game.time() > this.timer) {
    this.game.image.draw(this.tiles[this.game.animation.get("tileAnim")], x, y, null)
  }
}
public class Player() {
    public Player(Game g, int xpos, int ypos) {
      super(g);
      // do other stuff
    }

    public void jump() {
      // jump code
      Sound().play("jump");
    }
}
而不是:

public void draw(Graphics2D) {
  if (Game.time() > this.timer) {
    Image.draw(this.tiles[Animation.get("tileAnim")], x, y, null)
  }
}
甚至在地图编辑器中更糟:

   public void control() {
     if(this.map.game.input.keyPressed(...)) {
       this.map.game.sound.play(...);
     }
   }
编辑
基于这些答案,我决定创建一个GameObject类,为每个组件提供包装器方法。地图,玩家等。然后从它的子类,这样我所有的这个。游戏调用都隐藏在场景后面,它的正面看起来仍然不错:

public class GameObject {
    private Game game;

    public GameObject(Game g) {
        game = g;
    }

    public Game Game() {
        return game;
    }

    public GameAnimation Animation() {
        return game.animation;
    }

    public GameInput Font() {
        return game.input;
    }

    // ...

    public long Time() {
        return game.time();
    }
}
现在代码如下所示:

public void draw(Graphics2D) {
  if (this.game.time() > this.timer) {
    this.game.image.draw(this.tiles[this.game.animation.get("tileAnim")], x, y, null)
  }
}
public class Player() {
    public Player(Game g, int xpos, int ypos) {
      super(g);
      // do other stuff
    }

    public void jump() {
      // jump code
      Sound().play("jump");
    }
}
还是这更糟糕

EDIT2
好的,我已经在使用方法调用时遇到了问题,编译器给了我错误,因为它在原始的游戏中找不到我的子类游戏的方法,我想我将在这里使用普通字段

EDIT3
好的,我的GameObject类现在看起来是这样的,一切都正常了,我可以重新实现applet支持:)


static
与其他工具一样作为语义工具存在。当某个对象是静态的时,这对类试图建模的对象具有意义。在您的例子中,您正在有效地使用static来创建全局存储,并且您显然遇到了一些问题,提示全局存储的一般规则是“坏的”

如果你的静态界面的行为迫使你的设计问题和实现问题以你不希望的方式出现(比如你的applet支持),那么,是的,这是个问题,你应该考虑重构到一个更合适的设计。 你应该去掉所有的静力学吗?大概如果一个特定对象的多个实例在任何时候都不可能存在,并且您愿意处理同步访问该对象的成本,那么可能有些东西可以保持静态。不过,在你的例子中,我看不到任何基于我对它们的行为的猜测而绝对保持静态的东西


(关于您对链式对象引用的明显厌恶,您可能希望研究能够使代码更符合它的方法和技术。我不认为代码应该严格遵守它,但通常应该尝试使链式方法调用稍微短一些。)

在我看来,使用静态来共享某些全局状态,而不必将其注入客户机类,这会使代码更难测试、重用、破解等,因为您正在将这些客户机类耦合到它们正在使用的静态成员所在的类

下面是一个人为的例子:

如果,比方说,您提出了两种不同类型的游戏类,并希望对它们进行基准测试,会怎么样

或者,你可能想扩展你的引擎来运行两种完全不同的游戏(可能是平板游戏和射击游戏,谁知道呢),碰巧你可以通过修改游戏和保持所有其他类不变来做到这一点。(我说这是人为的。)

当所有其他类都访问Game类的静态成员时,您必须编写应用程序的两个版本,每个版本都有自己的Game实现


如果您只是将一个游戏对象传递到每个需要它的类中,那么您可以将游戏子类化,并将任何类型的游戏传递到需要它的客户端类中。

过多地使用静态通常会使代码的更改变硬,并降低其可测试性

考虑将静态对象替换为普通对象。您可以将它们传递到对象的构造函数中。这样,它们将很容易被另一个实现(用于测试的真实或模拟/存根)替换。这种技术被称为

例如,您向应用程序传递了三个对象:

  • gameTimer(从Game类中提取,使其不那么像上帝)
  • 形象
  • 动画
并将其保存到字段中。这样代替

public void draw(Graphics2D) {
  if (Game.time() > this.timer) {
    Image.draw(this.tiles[Animation.get("tileAnim")], x, y, null)
  }
}
你会有

public void draw(Graphics2D) {
  if (this.gameTimer.time() > this.timer) {
    this.image.draw(this.tiles[this.animation.get("tileAnim")], x, y, null)
  }
}
这种差异看起来很微妙,但实际上很重要,因为您的代码将变得更加模块化

  • 每个对象都有单独的责任,因此可以很好地进行测试
  • 如果要实现另一个版本的Animation类(Animation2),则只需要在main()函数中进行更改,而不需要在Animation类中进行更改
  • 只要看一眼代码就可以知道使用了哪些对象,无需查找静态方法调用

我个人在静力学方面的经验是双重的。首先,我发现静态是一种反模式,它经常混淆和隐藏我的设计有问题的事实。如果我发现自己需要一个静态变量,我必须问自己为什么。然而,有时需要静态文件,而且实际上是适当的。在这些情况下,我试图分离程序真正全局的部分,并将它们放在它们自己的单例类中,而不是放在某个地方的静态变量中。尽管这可能只是语义上的问题,但根据我的经验,让它们成为一个单例可以让事情在面向对象的意义上更干净一些。

首先

你不必仅仅因为在“纸面上”这样做会更好而抛弃所有的静态代码

您必须了解实例代码(非静态)和类代码(静态)之间的区别

静态当方法/属性不需要类的实例来工作时,使用代码(类方法/属性)。一个很好的例子是图像绘制方法:
image.draw()

实例方法
public void draw(Graphics2D) {
  if( this.game.needsDrawing() ) {
       this.game.draw();
  }
}
public void draw(Graphics2D) {
    if (this.game.time() > this.timer) {
        this.game.image.draw(this.tiles[this.game.animation.get("tileAnim")], x, y, null)
    }
}
class Game {
     GameState state = GameState.getInitialState( this );

     void run() {
         while( state.alive ) {
              do X Y Z
              state.updateState();
          }
     }
  }


class GameState {
    Game context;
    static GameState getInitialState( Game g ) {
        return new StartGameState(g);
    }
    void updateState();
}

class StartGameState {
     void updateState() {
         if( this.context.someCondition() ) {
             this.context.state = new MidGameState();
         }
     }
 }


class MidGameState {
     void updateState() {
         if( this.context.someOtherCondition() ) {
             this.context.state = new EndGameState();
         }
     }
 }

class EndGameState {
     void updateState() {
        Game over... 
     }
 }