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


64

以下の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アプリケーションに役立ちます。


9
リッチヒッキーは、そのような非慣用的なもの(すべての静的メソッド)を奨励します。関数型が好きなら、関数型言語を使用することもできます;)ソフトウェアのすべてがオブジェクトとしてモデル化されるとは限りません。
ジョブ

13
@ジョブ:私はこれがどのように機能するか本当にわかりません-それは手続き型です。
アーロンノート

2
@Job:このクラスは不変ではありません。
アーロンノート

3
@DonalFellows:もちろんできますが、C#とF#は両方とも混合パラダイムです。しかし、静的メソッド!=関数型プログラミングです。FPは、関数の受け渡し/チェーン、不変のデータ型、副作用の回避などを意味します。これは、OPのコードスニペットが行うこととはまったく逆です。
アーロンノート

4
「静的メソッドであるという彼の理由は軽量であり、サーバーの負荷を抑えることでWebアプリケーションにメリットをもたらします」。それは間違いです。サーバーの負荷を抑えますか?
mamcx

回答:


87

明示的な「this」パラメーターを使用した静的メソッドの最も明らかな問題に取り組みます。

  1. 仮想ディスパッチとその後のポリモーフィズムが失われます。派生クラスでそのメソッドをオーバーライドすることはできません。もちろん、あなたが宣言することができますnewstatic派生クラスの)メソッドが、それは全体のクラス階層を認識し、OOをすることになっている正確に何である、明示的なチェックとキャストを行う必要がありますアクセスするコードを回避します

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

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

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

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

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

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


5
最終行だけで+1。もっと多くの人が「静的メソッド」を考える前に「インスタンスメソッド」を考えることを望みます。
スチュアートレイランドコール

4
+1。ほとんどの場合、テストを作成することはできないため、静的メソッドを使用するとテストを作成することが不可能になります。コードの一部が静的メソッドに依存すると、それをno-テストでのop。.NETの世界での主な例はFileFileInfoなどのクラスですDirectoryInfo(実際、必要に応じて挿入してテストでモックアウトできるインターフェイスの背後にそれらを隠すライブラリがあります)。
SM

ポリモーフィズム/継承に関するあなたの論点は重要ではないと思います。コードの再利用に継承を使用すべきではないため、これは無関係です。継承部分も疑わしいです。ほとんどの現代言語では、メソッドシグネチャはそれ自体が多態的です。機能的なアプローチをとる場合は、メソッドの受け渡しを開始し、インターフェースに基づいて交換可能な単純なメソッドから複雑なメソッドを作成します。OOPの場合と同じように、単純に念頭に置いて設計することによって相殺されない静的メソッドには、基本的に欠点はありません。
サラ

@smそれは単に真実ではありません。静的であろうとなかろうと、単体テストに含めることができないものに強く依存している場合、それはテスト不可能な期間です。staticはこれを引き起こしません。静的メソッドは、db接続を表すクラスと同じ方法で注入できます。「静的」とは、「静的に加えて、グローバルな状態と外部リソースに影響を与えるハード隠された依存関係」を意味すると想定しています。不公平だ。
サラ

@kaiは、XとYが何であれ、Xの代わりにYを実行する静的メソッドの注入を試みます。ねじ込むために外部の何かに依存する必要はありません。長い計算Xを実行する必要のない側面をテストすることに興味があるかもしれません。ラッパーを作成せずに、どのように派手な静的メソッドを注入しますか?関数の受け渡しはすべての言語でサポートされているわけではありません。静的メソッドにはユースケースがあり、そうすることが理にかなっているときに使用します。これは、常に起こるように、「できるからといって必ずしもそうすべきではない」というケースです。
SM

13

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


FWIWでは、Cを使用するobj->type->mthd(obj, ...);と、次のような仮想ディスパッチを実行できます。これは、他の言語の仮想ディスパッチで発生する(ただし舞台裏で)ものとほぼ同じです。
ドナルドフェローズ

@Karl掘り下げを開始するために、何らかの書面による参照を提供してください
ホルヘラビン

GObjectを優れたリファレンス実装として見たいかもしれません。
カールビーレフェルト

10

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

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


2
これは必ずしも問題ではありません。一部の言語は、同じことを意図的に達成するために他のメカニズム(C ++およびC#の非仮想メソッド)を提供します-C#は、このアイデアをさらに拡張するための「密閉」メソッドも提供します。明らかに、これだけを行うのではなく、知っておくと良いテクニックです...
Shog9

@Steveは、両方のメソッドを直接呼び出すことができるため、置き換えられません。

@ Steve、Javaの静的メソッドはオーバーライドできません。

@Thorbjørn-最初に、質問にはJavaやその他の特定のOOP言語がタグ付けされていません。さらに重要なことは、静的メンバー関数をオーバーライドできるとは言いませんでした。私は類推を使用してポイントを作ろうとしていました。明らかに失敗したので、コメントは削除されました。
Steve314

2
ある意味では、これらはまだ「メソッドの新しい実装」です。標準的なOOPの意味ではありません。これは、「あなたの言葉遣いも完璧ではない、nur-nur-na-nur-nur」と言っているように感じます;-)
Steve314

7

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

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

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

一般的には、あなたが使用する必要がありstaticますが、1つ以上のパラメータを受け入れるユーティリティメソッドを作成している場合にのみ、クラスやメソッドを、そして、いくつかの種類のオブジェクトを返す任意の副作用なし(クラスの状態変数にすなわち変更)


1
私はOPが何をstatic意味するのか理解していますが、なぜすべてを静的として宣言しないのかを尋ねています。
エドS.

1
Subject -のインスタンスを渡しますが、暗黙的なパラメーターではなく、明示的なパラメーターとして渡しthisます。
スティーブ314

まあ、ええ...しかし、私はあなたのポイントが表示されません。
エドS.

@Ed-暗黙的なパラメーターを介して実行できる、明示的なパラメーターを使用してほとんど何でもできることのみthis。期待には違いがありますが、継承以外では、何ができるかには実際の違いはありません。たとえば、これらの非静的メンバーに、明示的なパラメーターを介してアクセスできます。
スティーブ314

私は、同じクラスで宣言された静的メソッド(C ++の場合と同様)を使用しても、C#の引数のプライベートメンバーを取得できないという前提で操作していました。なぜそう思ったのかはわかりませんが、間違っていました。
エドS.

3

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

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

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


1
あなたの「...は手続き型プログラミングへの先祖返りです...」とあなたはそれをとても悪いと思うように思わせます。ある意味ではオブジェクト指向の一部だと思います。
-NoChance

making ... unit testing much easier.いいえ、できません。静的メソッドから分離できないため、静的メソッドを呼び出すコードを単体テストできなくなります。静的メソッドの呼び出しは、そのクラスへのハードコードされた依存関係になります。
StuperUser

2

言語とあなたが何をしようとしているかにもよりますが、答えはそれほど大きな違いはないかもしれませんが、staticバージョンには少し混乱があります。

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

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


2

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

私は考えNEVERパラメータとして、クラス型を取った静的メソッドを宣言していません。それは単にコードを複雑にするだけで、利益は得られません。


3
あなたが渡した場合は、それを行う可能性がありますobjectstaticする方法、および新しいを返しますobject。機能プログラマーは常にこれを行います。
ロバートハーベイ

質問:Math静的クラスを「ゲインなしの複素数」と見なしますか、それともすべての数値型に絶対値、否定、加算、二乗、任意の根などを定義する仮想メソッドがある場合、それを好むでしょうか?そうは思いません。いくつかの不変条件を適用する必要がある非表示の可変状態を保存する必要がある場合、オブジェクトは適切です。型を操作するすべての考えられるメソッドを型自体にまとめます。これは、複雑で維持できないコードにつながるものです。ロバートに同意します。機能的なプログラマがどのように機能するかを見てみたいと思うかもしれません。
サラ

@kai私が言ったように、ほとんど。Mathクラスは合理的な例外ですが、数値型にアタッチすることは悪い考えではありません。
ローレンペクテル

2

ここには多くの素晴らしい答えがあり、それらは私が答えとして考え出すことができるものよりもはるかに洞察力があり、知識が豊富ですが、アドレスされていない小さなものがあると感じています:

「私たちのアーキテクトは、すべてのAPIにすべての静的メソッドを使用することを決定しました。静的メソッドであるという彼の理由は軽量であり、サーバーの負荷を抑えることでWebアプリケーションに役立ちます。」

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

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

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

これに関する悪魔の擁護者の演劇:さらに進んで、次のようなことを言うことができます:

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

  • コンストラクターの複雑さ、型の階層、他のインスタンスメンバー、その他の未知の要因に応じて、非静的メソッド呼び出しの余分なオーバーヘッドは異なり、非常に大きくなる可能性があります

実のところ、私見では、上記のポイント(および「より高速なので静的を使用しましょう」の一般的なアプローチ)は、ストローマンの引数/評価です。

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

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


2

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

C ++などの他のコミュニティでは、オブジェクト指向はより自由に解釈されます。一部の専門家がこのテーマを研究しており、実際には、無料の関数がカプセル化を改善すると結論付けています(強調は私のものです)。

クラスのカプセル化の量を測定する合理的な方法は、クラスの実装が変更された場合に破損する可能性のある関数の数をカウントすることであることがわかりました。そのため、n個のメンバー関数を持つクラスは、n + 1個のメンバー関数を持つクラスよりもカプセル化されていることが明らかになります。そして、この観察は、メンバー関数よりも非メンバー非友人関数を好むという私の議論を正当化するものです

スコット・マイヤーズ

C ++の用語に不慣れな場合:

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

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

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

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

ただし、クラスのパブリック API のみを使用する必要がある場合はいつでも使用する必要があります。


-3

Javaで...

静的メソッドは上書きされませんが、代わりに非表示になります。そのため、クラスを拡張する場合には大きな違いになります(問題?)。

一方で、Javaプログラマーは、理由を実際に理解することなく、すべてがオブジェクトであると考える傾向があることに気づき、同意しません。

クラス固有のコードには静的を使用する必要があります。静的では、継承ではうまく機能しません。

静的メソッドの使用の良い例は、副作用のない独立した関数です。

また、キーワードsynchronizedを参照してください...


2
クラスを拡張すると、この静的メソッドはどのように非表示になり、この違いと問題はどこで発生しますか?同期はこれにどのように適合しますか?静的および継承の問題はどこで発生しますか?コアJavaライブラリで静的メソッドの良い点と悪い点をいくつか提供し、それぞれがそうである理由を説明できますか?
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.