Java-完全に静的なクラスを持つことは悪い考えですか?


16

私は現在、より大きなソロプロジェクトに取り組んでおり、インスタンスを作成する理由がわからないクラスがいくつかあります。

たとえば、現在のダイスクラスはすべてのデータを静的に格納し、そのメソッドもすべて静的です。サイコロを転がして新しい値を取得したいときは、を使用するだけなので、初期化する必要はありませんDice.roll()

私はこのような主な機能を1つだけ持っているいくつかの類似したクラスを持ち、すべてのイベントを担当する一種の「コントローラー」クラスで作業を開始しようとしています(プレイヤーが移動したとき、そうです)、このクラスでも同じ考えに従うことができることがわかりました。これらの特定のクラスに対して複数のオブジェクトを作成する予定はないので、それらを完全に静的にすることは悪い考えでしょうか?

Javaに関しては、これが「悪い習慣」と見なされるのかと思っていました。私が見たものから、コミュニティはこのトピックに関して一種の分裂のように思われますか?とにかく、私はこれに関するいくつかの議論が大好きだし、リソースへのリンクも素晴らしいでしょう!


1
プログラムが完全に手続き型である場合。なぜJavaを選んだのですか?
ライヴ

12
これらのクラスの単体テストを書いてみると、人々が静的状態にアクセスする静的メソッドを好まない理由がわかります。
ジョーリSebrechts

@Laiv私はまだプログラミングの初心者で、C ++の1年ほどで、今学期にJavaクラスを受講し、特にグラフィックライブラリなど、Javaがもっと好きになり始めています。
-HexTeke


5
静的クラスについて。静的メソッドが純粋な場合(状態を保持せず、入力引数と戻り値の型を持ちます)。心配することは何もありません
Laiv

回答:


20

本当に静的な静的クラスには何も問題はありません。つまり、メソッドの出力が変化する原因となる内部状態はありません。

Dice.roll()1から6までの新しい乱数を単に返す場合、状態は変化しません。確かに、あなたはRandomインスタンスを共有しているかもしれませんが、定義による状態の変化は、出力が常に適切でランダムになるとは考えません。また、スレッドセーフであるため、問題はありません。

多くの場合、最終的な「ヘルパー」またはプライベートコンストラクターと静的メンバーを持つ他のユーティリティクラスが表示されます。プライベートコンストラクターにはロジックが含まれておらず、誰かがクラスをインスタンス化するのを防ぐためだけに使用されます。最後の修飾子は、これが派生したいクラスではないという考えを持ち帰ります。単なるユーティリティクラスです。適切に行われれば、静的で最終的なものではないシングルトンや他のクラスメンバが存在しないはずです。

これらのガイドラインに従い、シングルトンを作成していない限り、これにはまったく問題はありません。コントローラークラスについて言及しますが、これにはほぼ確実に状態の変更必要になるため、静的メソッドのみを使用することはお勧めしません。あなたは、静的なユーティリティクラスに大きく依存していることはできますが、できません作ること静的なユーティリティクラス。


クラスの状態の変化とは何ですか?乱数は定義上非決定的であるため、戻り値は頻繁に変更されるため、1秒間、乱数を除外します。

純粋な関数とは、決定論的な関数のことです。つまり、特定の入力に対して、1つだけの出力が得られます。静的メソッドを純粋な関数にする必要があります。Javaには、状態を保持するために静的メソッドの動作を微調整する方法がありますが、それらはほとんど良いアイデアではありません。メソッドをstaticとして宣言すると、典型的なプログラマーは、すぐにそれが純粋な関数であると想定します。予想される動作から逸脱することは、一般的に言えばプログラムでバグを作成する傾向があるため、避けるべきです。

シングルトンは、できる限り「純粋な関数」とは反対の静的メソッドを含むクラスです。単一の静的なプライベートメンバーは、クラスに対して内部的に保持されます。これは、インスタンスが1つだけであることを保証するために使用されます。これはベストプラクティスではなく、いくつかの理由で後で問題が発生する可能性があります。私たちが話していることを知るために、シングルトンの簡単な例を以下に示します。

// DON'T DO THIS!
class Singleton {
  private String name; 
  private static Singleton instance = null;

  private Singleton(String name) {
    this.name = name;
  }

  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton("George");
    }
    return instance;
  }

  public getName() {
    return name;
  }
}

assert Singleton.getInstance().getName() == "George"

7
ダブル6がロールされたときに何が起こるかをテストしたい場合、静的乱数ジェネレーターを備えた静的クラスがあるため、ここで立ち往生しています。したがってDice.roll()、「グローバルステートなし」ルールの有効な例外ではありません。
デビッドアルノ

1
@HexTeke回答を更新しました。
ニール

1
@DavidArno本当ですが、その時点で、への単一の呼び出しをテストしていると本当に困っていると思いますrandom.nextInt(6) + 1。;)
ニール

3
@Neil、おologiesび申し上げますが、私は自分自身をあまり説明しませんでした。乱数シーケンスをテストするのではなく、他のテストを支援するためにそのシーケンスに影響を与えています。たとえばRollAndMove()、ダブル6を指定したときに再度ロールをテストする場合、最も簡単で堅牢な方法は、いずれかDiceまたは乱数ジェネレーターをモックアウトすることです。エルゴはDice、静的ランダムジェネレーターを使用した静的クラスにはなりたくない。
デビッドアルノ

12
ただし、更新は頭の中で問題にぶつかります。静的メソッドは決定論的でなければなりません。副作用がないはずです。
デビッドアルノ

9

staticクラスの制限の例を挙げるために、あなたのゲーマーの何人かが彼らのサイコロにわずかなボーナスを得たいとしたらどうでしょうか?そして、彼らは大金を払っても構わないと思っています!:-)

はい、別のパラメーターを追加できます。そのためDice.roll(bonus)

後でD20が必要になります。

Dice.roll(bonus, sides)

ええ、しかし、一部のプレーヤーは「最高に能力のある」特技を持っているので、決して「手探り」することはできません(1をロール)。

Dice.roll(bonus, sides, isFumbleAllowed)

これは面倒になりつつありますか?


これは、これらの静的メソッドまたは通常の方法であるかどうかを問い、その取得乱雑に直交するように思える
JK。

4
@jk、私は彼の主張を理解したと思う。より多くの種類のサイコロを考える場合、Dice静的クラスを持つことは意味がありません。その場合、さまざまなサイコロオブジェクトを作成し、適切なOOPプラクティスでモデル化できます。
デリック

1
@TimothyTruckle私はそれについて異議を唱えていません。私が言っているのは、この答えが実際に質問に答えていないということだけです。
nvoigt

2
@nvoigt 「私はそれについて異議を唱えていません」 -そうですね。オブジェクト指向言語の最も強力な機能は多態性です。そして、静的アクセスは事実上それを使用することをまったく防ぎます。そしてあなたは正しい:静的アクセスと同様にnew Dice().roll(...)考慮れる必要がありますDice.roll(...)。その依存関係を注入する場合にのみメリットがあります。
ティモシートッケル

2
@jkの違いstaticは、すべての呼び出しで混乱が発生し、すべての呼び出しで必要な知識が必要になるため、アプリケーション全体にグローバルに影響が及ぶことです。OOP構造では、コンストラクター/ファクトリーのみが面倒である必要があり、そのダイの詳細はカプセル化されます。その後、ポリモーフィズムを使用して、を呼び出しますroll()。この答えを編集して明確にすることができます。
user949300

3

Diceクラスの特定のケースでは、静的メソッドではなくインスタンスメソッドを使用すると、テストが大幅に簡単になると思います。

Diceのインスタンス(Gameクラスなど)を使用する何かを単体テストする場合、テストから、常に固定値のシーケンスを返すDiceのテストダブルのフォームを挿入できます。あなたのテストは、それらのサイコロを振るのに適切な結果がゲームにあることを確認できます。

私はJava開発者ではありませんが、完全に静的なDiceクラスでこれを行うのはかなり難しいと思います。/programming/4482315/why-does-mockito-not-mock-static-methodsを参照してください


記録のためだけに:Javaには、バイトコードを操作して静的な依存関係を置き換えるPowerMockがあります。しかし...それは悪いデザインにちょうど降伏で使用して
ティモシーTruckle

0

これは実際にはMonostateパターンとして知られており、すべてのインスタンス(および「インスタンスなし」)がステータスを共有しています。すべてのメンバーはクラスメンバーです(つまり、インスタンスメンバーはありません)。それらは一般に、単一の責任または要件に関連するメソッドと定数のセットをバンドルする「ツールキット」クラスを実装するために使用されますが、動作するために状態を必要としません(純粋に機能します)。実際、Javaにはそれらの一部がバンドルされています(たとえば、Math)。

オフトピックビット:私はめったにVisualBasicの中のキーワードの命名に同意していないが、この場合には、私が考えてshared(それがさ、確かに明確で、意味的に優れた共有よりも、クラス自体とそのすべてのインスタンス間で)staticライフサイクルの後に残ります(で宣言されているスコープの)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.