118

クラスが内部状態を追跡する必要がない場合、クラス内のすべてのメソッドを静的として宣言する傾向があります。たとえば、A を B に変換する必要があり、変化する可能性のある内部状態 C に依存しない場合は、静的変換を作成します。調整できるようにしたい内部状態 C がある場合は、コンストラクターを追加して C を設定し、静的変換は使用しません。

静的メソッドを過度に使用しないようにというさまざまな推奨事項 (StackOverflow を含む) を読みましたが、上記の経験則で何が問題なのかまだ理解できていません。

それは合理的なアプローチですか?

4

15 に答える 15

174

一般的な静的メソッドには次の 2 種類があります。

  • 「安全な」静的メソッドは、同じ入力に対して常に同じ出力を提供します。グローバルを変更せず、どのクラスの「安全でない」静的メソッドも呼び出しません。基本的に、限定された種類の関数型プログラミングを使用しています。これらを恐れないでください。問題ありません。
  • 「安全でない」静的メソッドは、グローバル状態を変更したり、グローバル オブジェクトにプロキシしたり、その他のテスト不可能な動作を行ったりします。これらは手続き型プログラミングへの回帰であり、可能であればリファクタリングする必要があります。

"安全でない" 静的変数の一般的な使用法がいくつかあります (たとえば、Singleton パターンなど)。安全でない静的を使用する前に、慎重に検討してください。

于 2009-04-15T17:22:30.753 に答える
22

内部状態のないオブジェクトは疑わしいものです。

通常、オブジェクトは状態と動作をカプセル化します。動作をカプセル化するだけのオブジェクトは奇妙です。場合によっては、軽量またはフライウェイトの例です。

また、オブジェクト言語で行われる手続き型の設計である場合もあります。

于 2009-04-15T17:16:41.720 に答える
19

これは、John Millikin のすばらしい回答のフォローアップにすぎません。


ステートレス メソッド (ほぼ関数) を静的にすることは安全ですが、変更が困難な結合につながる場合があります。次のような静的メソッドがあるとします。

public class StaticClassVersionOne {
    public static void doSomeFunkyThing(int arg);
}

あなたは次のように呼びます:

StaticClassVersionOne.doSomeFunkyThing(42);

静的メソッドの動作を変更しなければならない場合に出くわし、StaticClassVersionOne. おそらくコードを変更でき、それで問題ありませんが、古い動作に依存する他の呼び出し元があった場合は、メソッドの本体でそれらを考慮する必要があります。場合によっては、これらすべての動作のバランスをとろうとすると、メソッド本体がかなり醜くなったり、保守できなくなったりすることがあります。メソッドを分割すると、それを考慮していくつかの場所でコードを変更するか、新しいクラスを呼び出す必要がある場合があります。

しかし、メソッドを提供するためにインターフェースを作成し、それを呼び出し元に渡した場合を考えてみてください。動作を変更する必要がある場合、新しいクラスを作成してインターフェースを実装できます。これは、よりクリーンで、テストがより簡単で、保守が容易です。代わりに、それが呼び出し元に渡されます。このシナリオでは、呼び出し元のクラスを変更したり再コンパイルしたりする必要はなく、変更はローカライズされます。

起こりそうな状況かもしれませんし、そうでないかもしれませんが、検討する価値はあると思います。

于 2009-04-15T18:30:09.920 に答える
6

もう 1 つのオプションは、元のオブジェクトに非静的メソッドとして追加することです。

つまり、次のように変更します。

public class BarUtil {
    public static Foo transform(Bar toFoo) { ... }
}

の中へ

public class Bar {
    ...
    public Foo transform() { ...}
}

ただし、多くの場合、これは不可能です (たとえば、XSD/WSDL/etc からの通常のクラス コードの生成)。または、クラスが非常に長くなり、複雑なオブジェクトの場合、変換メソッドはしばしば本当に苦痛であり、それらが必要な場合があります。自分の別のクラスで。そうそう、私はユーティリティ クラスに静的メソッドを持っています。

于 2009-04-15T17:19:05.957 に答える
6

それは合理的なアプローチのようです。あまりにも多くの静的クラス/メソッドを使用したくない理由は、オブジェクト指向プログラミングから離れ、構造化プログラミングの領域に移行することになるからです。

A を B に変換するだけの場合、テキストを変換するだけだとします。

"hello" =>(transform)=> "<b>Hello!</b>"

次に、静的メソッドが理にかなっています。

ただし、オブジェクトでこれらの静的メソッドを頻繁に呼び出していて、多くの呼び出しで一意になる傾向がある場合 (たとえば、使用方法が入力に依存する場合)、またはオブジェクト固有の動作の一部である場合は、それをオブジェクトの一部にして、その状態を維持するのが賢明です。これを行う 1 つの方法は、インターフェイスとして実装することです。

class Interface{
    method toHtml(){
        return transformed string (e.g. "<b>Hello!</b>")
    }

    method toConsole(){
        return transformed string (e.g. "printf Hello!")
    }
}


class Object implements Interface {
    mystring = "hello"

    //the implementations of the interface would yield the necessary 
    //functionality, and it is reusable across the board since it 
    //is an interface so... you can make it specific to the object

   method toHtml()
   method toConsole()
}

編集: 静的メソッドの優れた使用例の 1 つは、Asp.Net MVC または Ruby の html ヘルパー メソッドです。これらは、オブジェクトの動作に関連付けられていない、したがって静的な html 要素を作成します。

編集2:関数型プログラミングを構造化プログラミングに変更しました(何らかの理由で混乱しました)、それを指摘してくれたTorstenへの小道具。

于 2009-04-15T17:15:13.533 に答える
5

静的メソッドを使用しないように警告されている理由は、静的メソッドを使用するとオブジェクトの利点の 1 つが失われるからです。オブジェクトは、データのカプセル化を目的としています。これにより、予期しない副作用が発生するのを防ぎ、バグを回避します。静的メソッドにはカプセル化されたデータ*がないため、この利点は得られません。

とはいえ、内部データを使用しない場合は、問題なく使用でき、実行もわずかに高速です。ただし、それらのグローバルデータに触れていないことを確認してください。

  • 一部の言語には、データと静的メソッドのカプセル化を可能にするクラスレベルの変数もあります。
于 2009-04-15T17:21:27.467 に答える
4

最近、アプリケーションをリファクタリングして、最初は静的クラスとして実装されていたいくつかのクラスを削除/変更しました。時間が経つにつれて、これらのクラスは非常に多くのものを獲得し、人々は新しい関数を static としてタグ付けし続けました。

したがって、私の答えは、静的クラスは本質的に悪いわけではありませんが、今すぐインスタンスの作成を開始し、後でリファクタリングする方が簡単かもしれないということです。

于 2009-04-15T17:21:37.560 に答える
3

もちろん、特効薬はありません。静的クラスは、小さなユーティリティ/ヘルパーには問題ありません。しかし、ビジネス ロジック プログラミングに静的メソッドを使用することは確かに悪いことです。次のコードを検討してください

   public class BusinessService
   {

        public Guid CreateItem(Item newItem, Guid userID, Guid ownerID)
        {
            var newItemId = itemsRepository.Create(createItem, userID, ownerID);
            **var searchItem = ItemsProcessor.SplitItem(newItem);**
            searchRepository.Add(searchItem);
            return newItemId;
        }
    }

ItemsProcessor.SplitItem(newItem);においがする原因への静的メソッド呼び出しが表示されます

  • 明示的な依存関係が宣言されていないため、コードを掘り下げないと、クラスと静的メソッド コンテナーの間の結合を見落とす可能性があります。
  • BusinessServiceそれを分離してテストすることはできずItemsProcessor(ほとんどのテスト ツールは静的クラスをモックしません)、単体テストが不可能になります。単体テストなし == 低品質
于 2015-09-10T12:55:31.853 に答える
3

デザインの匂いだと思います。ほとんどの静的メソッドを使用していることに気付いた場合は、おそらくあまり優れた OO 設計を行っていません。必ずしも悪いわけではありませんが、すべての匂いと同様に、立ち止まって再評価する必要があります. より良い OO 設計を作成できる可能性があること、またはこの問題については別の方向に進んで OO を完全に回避する必要があることを示唆しています。

于 2009-04-15T18:50:58.560 に答える
1

内部状態が機能しない限り、これで問題ありません。通常、静的メソッドはスレッド セーフであることが期待されるため、ヘルパー データ構造を使用する場合は、スレッド セーフな方法で使用してください。

于 2009-04-15T17:16:58.117 に答える
1

C の内部状態を使用する必要がないことがわかっている場合は、問題ありません。ただし、将来これが変更された場合は、メソッドを非静的にする必要があります。そもそも静的でない場合、必要がなければ内部状態を無視できます。

于 2009-04-15T17:23:46.337 に答える
1

ユーティリティメソッドなら、静的にするといいです。Guava と Apache Commons は、この原則に基づいて構築されています。

これに関する私の意見は純粋に実用的です。それがアプリ コードの場合、静的メソッドは一般的に最適なものではありません。静的メソッドには重大な単体テストの制限があります。簡単にモックすることはできません。モックした静的機能を他のテストに挿入することはできません。通常、静的メソッドに機能を注入することもできません。

したがって、私のアプリ ロジックでは、通常、小さな静的ユーティリティのようなメソッド呼び出しがあります。いえ

static cutNotNull(String s, int length){
  return s == null ? null : s.substring(0, length);
}

利点の1つは、そのような方法をテストしないことです:-)

于 2013-09-20T10:41:31.673 に答える