1

まず第一に、私の英語が完璧でなくてすみませんが、私は英語圏の国(スペイン)の出身ではないので...

さて、ここで質問です。クラスを作成するとき、物事を明確にするために、可能な限り一時変数を使用することをお勧めしますか、それとも変数をクラス変数として宣言することをお勧めしますか?

単純なクラス SpriteSheet を使用した例を示します。Java のほとんどすべての 2D ゲームで使用される、非常に短く人気のあるクラスです。

これは、私が見ていたチュートリアルの作成者によって最初に計画されたとおりのコードです。

public class SpriteSheet {

private String path;
private final int SIZE;
public int[] spriteSheetPixels;

public SpriteSheet(String path, int size) {
this.path = path;
SIZE = size;

spriteSheetPixels = new int[SIZE * SIZE];

load();
}

private final void load() {
try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));
    int w = image.getWidth();
    int h = image.getHeight();
    image.getRGB(0, 0, w, h, spriteSheetPixels, 0, w);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

ポイントは、彼は私の知る限りすべての Java 規則に従って、通常のクラスを行っているということです。それを見て、もう少し改善できると思いました。まったく同じクラスの私のバージョンは次のとおりです。

public final class SpriteSheet {

public final int[] spriteSheetPixels;

public SpriteSheet(final String path, final int width, final int height) {
spriteSheetPixels = new int[width * height];

load(path, width, height);
}

private final void load(final String path, final int width, final int height) {
try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));

    final int w = image.getWidth();
    final int h = image.getHeight();
    final byte ZERO = 0;

    image.getRGB(ZERO, ZERO, w, h, spriteSheetPixels, ZERO, w);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

念のために、あまり注意を払いたくない場合は、変更した内容とその理由を再開してみます: - クラス宣言に "final" を追加しました。インスタンス化します。- 配列を除くすべてのクラス変数を削除しました。これは、このクラスから使用する唯一のものであるためです。残りの変数をクラス変数として宣言するのは、単なるメモリの無駄だと思います。それらが一時的なものである場合、私が間違っていなければ、それらは使用され、GC は遅かれ早かれそれらを処理し、メモリを解放します。- ランタイムの残りの部分はそのままになるため、配列を final としてマークしました。- 正方形でないスプライト シートを使用する場合に備えて、SIZE 定数を幅と高さに分割します。- w と h を宣言するのは実際には良い考えです。パラメータでメソッドを呼び出すと、通常、実行速度が低下するためです(または、それは私がいくつかの場所で読んだことです)。- 0 は数回使用されるため、変数として宣言すると実行速度が向上すると思います (ほんの少しですが、とにかく目立たないでしょう)。

そして、それは基本的にすべてです。私は学生であり、おそらく私はいくつかの非常にn00bの間違いを犯していることに注意してください。経験豊富なプログラマーがたくさんいると確信しているので、ここで質問したかったのです.

SpriteSheet クラスについてはあまり気にしませんが、最適化の品質についてもっと知りたいと思っています。

私は物事を改善しましたか、それとも最悪の状態にしましたか (物事を実際に遅くしたり、読みにくくしたり、将来の保守を難しくしたり、とにかくコンパイラーが行うことを行ったりしました...)?

私の質問が長すぎて漠然としすぎている場合は申し訳ありませんが、私の最初の質問は簡単です;)

前もって感謝します。

編集:

少し休憩してから読んだだけですが、意味がありません (幅と高さのパラメーターを解析して load() を使用していて、それらを使用していないことがわかりましたか?)

これは私がそうあるべきだと私が信じているとおりです:

public final class SpriteSheet {

public final int[] spriteSheetPixels;

public SpriteSheet(final String path, final int width, final int height) {
final byte ZERO = 0;
spriteSheetPixels = new int[width * height];

try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));

    image.getRGB(ZERO, ZERO, width, height, spriteSheetPixels, ZERO,
        width);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

メソッドが本当に必要ないことに気づきました。コンストラクターですべてを実行できます。

4

3 に答える 3

0

クラス/変数ではなく、例のようにインスタンス変数を意味すると仮定しますstatic。(本当にstatic変数を意味するのであれば、対処すべき問題は他にもたくさんあります...)


この質問の「最適化」の側面から始めましょう。

最初に言うことは、2 つのアプローチの違いはおそらく取るに足らないということです。プログラムのパフォーマンスに顕著な違いをもたらさない可能性があります。あなたの場合、一度にそのクラスのインスタンスが最大で数十個あると思います。そのため、メモリ使用量の違いは最大で数千バイトになります。

とは言え、違いはあります。フィールドをインスタンス フィールドとして宣言すると、それらはオブジェクトの存続期間中存在します (そしてヒープ メモリを占有します)。対照的に、ローカル変数は、外側のメソッド呼び出しが終了すると存在しなくなります。したがって、ローカル変数を使用すると、長期的に使用するメモリが少なくなる可能性があります。


しかし、ここでの重要な問題は、可読性、保守性、および正確性です。

ローカルの「一時的な」変数をインスタンス変数に変換すると、さまざまなメソッドのスコープがあります...または同じメソッドへのさまざまな呼び出し...インスタンス変数の使用を介して互いに干渉します。一部のユースケースでは、干渉が避けられないことに注意してください。たとえば、2 つの異なるスレッドが同じオブジェクトの同じメソッドを同時に呼び出す場合、またはメソッドが直接的または間接的にそれ自体を呼び出す場合などです。つまり、再帰。

そして、そのようなこと起こる可能性があるという事実は、コードを読みにくくし、維持するのを難しくします。(コードに本当に精通しており、誰かがコードに加えたすべての変更を追跡していない限り...何かがあなたの仮定を破ったことを確認することはできません...そして確認する必要があります.)

対照的に、ローカル変数ではこれらの問題はありません。それらは、現在のスレッドまたは別のスレッドで、他のメソッド呼び出しから見えないことが保証されています。

要するに、 「物事を明確にするためだけに」変数をインスタンス変数として宣言すると、実際には逆の効果があります。それは物事をかなり不明確にするでしょう。


最後に、これに関連する興味深い質問が Programmers サイトにありました。

私の回答での私の結論は、これはデザイン パターンではないため、実際には「アンチ パターン」とはみなされないというものでした。しかし、それにもかかわらず、それは本当に悪いです。

于 2014-03-30T02:33:26.807 に答える