web-dev-qa-db-ja.com

すべての静的メソッドを使用することはできませんか?

以下の2つのUpdateSubjectメソッドの違いは何ですか?エンティティを操作するだけの場合は、静的メソッドを使用する方が良いと感じました。どのような状況で非静的メソッドを使用する必要がありますか?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

私はこの本当に迷惑な質問のためにコミュニティから多くのキックを受け取ることになることを知っていますが、私はそれを尋ねるのを止めることができませんでした。

これは継承を扱うときに非現実的になりますか?

更新:
現在、私たちの職場で起こっています。私たちは、5人の開発者がいる6か月のasp.net Webアプリケーションに取り組んでいます。私たちの設計者は、すべてのAPIにすべての静的メソッドを使用することを決定しました。静的メソッドであるという彼の推論は軽量であり、サーバーの負荷を低く保つことでWebアプリケーションに利益をもたらします。

65
Alexander

明示的な「this」パラメーターを持つ静的メソッドの最も明白な問題を取り上げます。

  1. 仮想ディスパッチと、それに続く多態性が失われます。派生クラスでそのメソッドをオーバーライドすることはできません。もちろん、派生クラスでnewstatic)メソッドを宣言できますが、それにアクセスするコードは、クラス階層全体を認識し、明示的なチェックとキャストを行う必要があります。正確には、OOがavoidと想定されているもの)。

  2. #1の拡張のように、クラスのインスタンスをinterfaceで置き換えることはできません。これは、インターフェース(ほとんどの言語)が宣言できないためです。 staticメソッド。

  3. 不要な冗長性。どちらがより読みやすいですか:Subject.Update(subject)または単にsubject.Update()

  4. 引数のチェック。再び言語に依存しますが、多くの場合、暗黙的なチェックをコンパイルしてthis引数がnullではないことを確認し、null参照バグが安全でないランタイム条件(バッファーの種類)を作成しないようにしますオーバーラン)。インスタンスメソッドを使用しない場合、このチェックをすべてのメソッドの先頭に明示的に追加する必要があります。

  5. 紛らわしいです。普通の合理的なプログラマがstaticメソッドを見ると、当然それを想定します有効ではないインスタンスは必要ありません(比較または等価メソッドのように複数のインスタンスを必要とする場合、またはnull参照を操作できることが期待される場合を除く)。この方法で使用される静的メソッドを見ると、ダブルテイクまたはおそらくトリプルテイクが実行されます。4回目または5回目以降は、ストレスがたまり、怒り、神があなたの自宅の住所を知っていれば、神が助けてくれます。

  6. それは複製の一種です。インスタンスメソッドを呼び出すときに実際に何が起こるかは、コンパイラまたはランタイムが型のメソッドテーブルでメソッドを検索し、thisを引数として使用して呼び出すことです。基本的には、コンパイラがすでに行っていることを再実装しています。 [〜#〜] dry [〜#〜] に違反していて、同じパラメーターが不要な場合は、別のメソッドで何度も繰り返します。

インスタンスメソッドを静的メソッドに置き換える良い理由を考えるのは困難です。しないでください。

87
Aaronaught

OOP Cでは静的メソッドしか使用できず、 "this"は構造体ポインタです)を正確に説明してきました。そして、はい、実行時のポリモーフィズムはCは、構築中に構造体の1つの要素として格納される関数ポインターを指定します。他の言語で使用できるインスタンスメソッドは、基本的には内部でまったく同じことを行う構文上の糖衣です。Pythonなどの一部の言語は、その中間にあります。 「self」パラメータは明示的にリストされていますが、それを呼び出すための特別な構文は継承とポリモーフィズムを処理します。

13
Karl Bielefeldt

静的メソッドの問題は、サブクラスが必要になったときに発生します。

静的メソッドはサブクラスでオーバーライドできないため、新しいクラスはメソッドの新しい実装を提供できず、それらの有用性が低くなります。

10
user1249

メソッドstaticを宣言する場合、メソッドを実行するために(newキーワードを使用して)クラスからオブジェクトをインスタンス化する必要はありません。ただし、メンバー変数を参照することはできませんそれらも静的でない限りこの場合、それらのメンバー変数クラスに属するクラスの特定のインスタンス化されたオブジェクトではありません。

言い換えると、staticキーワードを使用すると、宣言がクラス定義の一部になるため、-クラスのすべてのインスタンスにわたる単一の参照ポイント(クラスからインスタンス化されたオブジェクト)になります。 。

したがって、クラスの複数の実行中のコピーが必要で、実行中のすべてのコピー間で状態(メンバー変数)を共有したくない場合(つまり、各オブジェクトが独自の固有の状態を保持している場合)は、これらのメンバー変数を静的に宣言することはできません。

一般に、staticクラスとメソッドは、1つ以上のパラメーターを受け入れ、何らかのオブジェクトを返すユーティリティメソッドを作成する場合にのみ使用してください副作用なし(つまりクラスの状態変数の変更)

7
Robert Harvey

「静的メソッドが悪いデザインを示している」と人々が主張する理由は、必ずしもメソッドに起因するものではなく、それは実際には暗黙的または明示的な静的データです。暗黙的とは、戻り値またはパラメーターに含まれていないプログラムの任意の状態を意味します。静的関数の状態の変更は、手続き型プログラミングへの逆戻りです。ただし、関数が状態を変更しない場合、または状態の変更をコンポーネントオブジェクト関数に委任する場合、実際には手続き型よりも機能パラダイムです。

状態を変更しない場合、静的メソッドとクラスには多くの利点があります。これにより、メソッドが純粋であることをはるかに簡単に確認できるため、副作用やエラーが完全に再現されます。また、共有ライブラリを使用する複数のアプリケーションの異なるメソッドが同じ入力で同じ結果を得ることが保証され、エラー追跡とユニットテストの両方がはるかに簡単になります。

最終的には、「StateManager」などの一般的に見られる特大のオブジェクトと静的データまたはグローバルデータの間に実際の違いはありません。正しく使用される静的メソッドの利点は、後で改訂者に状態を変更しないという作成者の意図を示すことができることです。

3
Mathieson

これは非常に興味深い質問であり、質問するコミュニティによって回答が大きく異なる傾向があります。他の答えは、C#またはJavaバイアス:(ほとんど)純粋なオブジェクト指向であり、オブジェクト指向とは何かの慣用的な見方があるプログラミング言語)のようです。

C++などの他のコミュニティでは、オブジェクト指向はより自由に解釈されます。専門家がこの問題を研究していることをよく知っている人もいますが、実際には 自由関数はカプセル化を改善する (強調は私のものです)

クラスのカプセル化の量を測定する合理的な方法は、クラスの実装が変更された場合に機能しなくなる可能性のある関数の数を数えることです。その場合、n個のメンバー関数を持つクラスは、n + 1個のメンバー関数を持つクラスよりもカプセル化されることが明らかになります。そして、その観察は私の引数を正当化するものですメンバー関数よりも非メンバー非フレンド関数を好む

スコット・マイヤーズ

C++用語に不慣れな方のために:

  • メンバー関数=メソッド
  • 非メンバー関数=静的メソッド
  • 非友人=パブリックAPIのみを使用
  • 非メンバー非フレンド関数=パブリックAPIのみを使用する静的メソッド

今、あなたの質問に答えるために:

すべての静的メソッドを使用することはできませんか?

いいえ、常に静的メソッドを使用する必要はありません。

ただし、クラスのpublicAPIのみを使用する必要がある場合は常にそれらを使用する必要があります。

3
authchir

言語と何をしようとしているのかによって、大きな違いはないという答えが出るかもしれませんが、staticバージョンはもう少し複雑です。

例の構文がJavaまたはC#を示唆しているように、ThorbjørnRavn Andersenがオーバーライド(遅延バインディングを使用)の問題を指摘するのは正しいと思います。静的メソッドでは、 "特別な"パラメータはありません遅延バインディングに基づくため、遅延バインディングを使用できません。

実際、静的メソッドはモジュール内の関数であり、そのモジュールはクラスと同じ名前を持っています-実際にはOOPメソッドではありません。

2
Steve314

これを行うと、基本的にオブジェクトのポイント全体を無効にします。手続き型コードからオブジェクト指向コードにかなりシフトしたのには、十分な理由があります。

私は[〜#〜] never [〜#〜]クラス型をパラメータとして受け取る静的メソッドを宣言します。それだけでは、コードはより複雑になり、利益がありません。

2
Loren Pechtel

ここには素晴らしい答えがたくさんあり、それらは答えとして思いつくことができたものよりもはるかに洞察力があり、知識が豊富ですが、私は対処されていない何かがあると感じています:

「すべてのAPIにすべての静的メソッドを使用することをアーキテクトは決定しました。静的メソッドであるという彼の推論は軽量であり、それはサーバーの負荷を低く抑えることによってWebアプリケーションにメリットをもたらします。」

(大胆な強調は私のものです)

質問のこの部分に関する私の2cはこれです:

理論的には、そのとおりです。静的メソッドを呼び出すと、そのメソッドを実際に呼び出すだけのオーバーヘッドが発生します。非静的(インスタンス)メソッドを呼び出すと、最初にオブジェクトをインスタンス化し、ある時点でインスタンスを破棄するというオーバーヘッドが余分にかかります(使用するプラットフォームに応じて、手動または何らかの形式の自動ガベージコレクションを介して)。

これについて、悪魔の支持者を少し演じます。さらに進んで、次のようなことを言うことができます。

  • (インスタンス)メソッドの呼び出しごとにインスタンスが作成されると、これは本当に悪くなる可能性があります(静的なままにして、そのように呼び出すのではなく)

  • コンストラクターの複雑さ、型階層、他のインスタンスメンバー、およびその他の不明な要素に応じて、非静的メソッド呼び出しの追加のオーバーヘッドは変化し、実際に大きくなる可能性があります

実際、IMHO、上記の点(および「より高速なため、静的を使用しましょう」という一般的なアプローチ)は、ストローマンの引数/アセンブリです。

  • コードが適切で、この引数により具体的であり、インスタンスが必要な場合にのみ作成される(そして最適な方法で破棄される)場合、適切な場合にインスタンス化メソッドを使用することによる余分なオーバーヘッドは発生しません(必要なオブジェクトのみを作成します。そのメソッドが静的として宣言されていても作成されます。

  • この方法で静的メソッド宣言を悪用することにより、インスタンスの作成方法に関するコードのいくつかの問題を隠すことができます(メソッドが静的であるため、不適切なインスタンス化コードが気付かれずに後で通過する可能性があり、それは決して良くありません)。

2
Shivan Dragon