182

単一の機能を実行するためのクラスがあるとします。機能を実行した後、破壊することができます。これらのアプローチのいずれかを好む理由はありますか?

// Initialize arguments in constructor
MyClass myObject = new MyClass(arg1, arg2, arg3);
myObject.myMethod();

// Pass arguments to method
MyClass myObject = new MyClass();
myObject.myMethod(arg1, arg2, arg3);

// Pass arguments to static method
MyClass.myMethod(arg1, arg2, arg3);

さまざまな状況でのガイドラインを得るために、意図的に詳細を曖昧にしました。しかし、Math.random() のような単純なライブラリ関数はまったく考えていませんでした。特定の複雑なタスクを実行するクラスをもっと考えていますが、それを行うには1つの(パブリック)メソッドしか必要としません。

4

15 に答える 15

266

私は静的メソッドで満たされたユーティリティ クラスが大好きでした。彼らはヘルパー メソッドを大幅に統合しました。そうでなければ冗長性とメンテナンス地獄を引き起こします。それらは非常に使いやすく、インスタンス化も廃棄も必要ありません。これは、サービス指向アーキテクチャを作成する最初の無意識の試みだったと思います。多くのステートレス サービスは、ただ自分の仕事だけを行い、他には何もしませんでした。しかし、システムが成長するにつれて、ドラゴンがやってくる。

ポリモーフィズム
喜んで賑わう UtilityClass.SomeMethod メソッドがあるとします。突然、機能を少し変更する必要があります。ほとんどの機能は同じですが、いくつかの部分を変更する必要があります。静的メソッドでなければ、派生クラスを作成し、必要に応じてメソッドの内容を変更できます。これは静的メソッドであるため、できません。確かに、古いメソッドの前または後に機能を追加する必要がある場合は、新しいクラスを作成し、その中で古いクラスを呼び出すことができますが、それは大雑把です。

インターフェイス
の問題 ロジック上の理由から、インターフェイスを介して静的メソッドを定義することはできません。また、静的メソッドをオーバーライドできないため、静的クラスをインターフェイスで渡す必要がある場合、静的クラスは役に立ちません。これにより、静的クラスを戦略パターンの一部として使用できなくなります。インターフェイスの代わりにデリゲートを渡すことで、いくつかの問題を修正する可能性があります。

テスト
これは基本的に、上記のインターフェイスの問題と密接に関連しています。実装を交換する能力は非常に限られているため、製品コードをテスト コードに置き換えるのも困難です。繰り返しますが、それらをラップすることはできますが、実際のオブジェクトの代わりにラッパーを受け入れることができるようにするためだけに、コードの大部分を変更する必要があります。

ブロブ
の育成 静的メソッドは通常、ユーティリティ メソッドとして使用され、ユーティリティ メソッドは通常、異なる目的を持つため、すぐに一貫性のない機能で満たされた大きなクラスになってしまいます。理想的には、各クラスはシステム内で 1 つの目的を持つ必要があります。 . 目的が明確に定義されている限り、クラスの 5 倍を使用したいと思います。

パラメータクリープ
まず、この小さくて無邪気な静的メソッドは、単一のパラメーターを受け取る場合があります。機能が拡大するにつれて、いくつかの新しいパラメーターが追加されます。オプションのパラメーターがすぐに追加されるので、メソッドのオーバーロードを作成します (または、サポートする言語ではデフォルト値を追加するだけです)。やがて、10 個のパラメーターを受け取るメソッドが完成します。実際に必要なのは最初の 3 つだけで、パラメーター 4 から 7 はオプションです。しかし、パラメーター 6 が指定されている場合は、7 ~ 9 も入力する必要があります... この静的メソッドが行ったことを行うことを 1 つの目的としてクラスを作成した場合、必要なパラメーターをコンストラクター、およびユーザーがプロパティを介してオプションの値を設定できるようにするか、複数の相互に依存する値を同時に設定するメソッドを許可します。また、メソッドがこの程度の複雑さに成長した場合、

消費者に理由もなくクラスのインスタンスを作成するよう要求する
最も一般的な議論の 1 つは、クラスのコンシューマーがこの単一のメソッドを呼び出すためのインスタンスを作成することを要求するのに、後でインスタンスを使用しないのはなぜですか? クラスのインスタンスを作成することは、ほとんどの言語で非常に安価な操作であるため、速度は問題になりません。消費者に余分なコード行を追加することは、将来的にはるかに保守しやすいソリューションの基盤を築くための低コストです。最後に、インスタンスの作成を避けたい場合は、簡単に再利用できるクラスのシングルトン ラッパーを作成するだけです。ただし、これにより、クラスがステートレスであることが要件になります。ステートレスでない場合でも、すべてを処理する静的ラッパー メソッドを作成できますが、長期的にはすべてのメリットが得られます。ついに、


もちろん、静的メソッドが嫌いな私には例外もあります肥大化のリスクをもたらさない真のユーティリティ クラスは、静的メソッドの優れたケースです (例として System.Convert)。プロジェクトが将来のメンテナンスの必要がない 1 回限りのものである場合、全体的なアーキテクチャはそれほど重要ではありません。静的か非静的かは問題ではありませんが、開発速度は重要です。

スタンダード、スタンダード、スタンダード!
インスタンス メソッドを使用しても、静的メソッドの使用が妨げられることはありません。その逆も同様です。差別化の背後に理由があり、それが標準化されている限り。さまざまな実装方法が無秩序に広がるビジネス層を見渡すことほど悪いことはありません。

于 2008-10-15T21:03:38.523 に答える
93

私は静的な方法を好みます。クラスはオブジェクトを表していないため、そのインスタンスを作成しても意味がありません。

メソッドのためだけに存在するクラスは、静的のままにしておく必要があります。

于 2008-10-15T17:44:54.430 に答える
18

関数を実行するためにクラスのインスタンスを作成する理由がない場合は、静的実装を使用します。インスタンスが必要ないのに、なぜこのクラスのコンシューマにインスタンスを作成させるのですか?

于 2008-10-15T17:46:11.027 に答える
16

オブジェクトの状態を保存する必要がない場合は、最初からインスタンス化する必要はありません。パラメータを渡す単一の静的メソッドを使用します。

また、関連のない多数の静的メソッドを持つ巨大な Utils クラスについても警告します。これは、急いで混乱して扱いにくくなる可能性があります。それぞれに関連するメソッドがほとんどない、多くのクラスを用意することをお勧めします。

于 2008-10-15T18:24:30.403 に答える
6

私は静的メソッド形式がより良い選択肢だと思います。また、クラスも静的にします。そうすれば、誤ってクラスのインスタンスを作成することを心配する必要がなくなります。

于 2008-10-15T17:46:37.640 に答える
5

ここでの状況は本当にわかりませんが、arg1、arg2、またはarg3が属するクラスの1つにメソッドとして配置することを検討します-これらのクラスの1つが方法。

于 2008-10-15T17:46:27.453 に答える
4

提供された情報に基づいて回答するのは難しいことをお勧めします。

私の直感は、メソッドが 1 つだけで、クラスをすぐに破棄する場合は、すべてのパラメーターを受け取る静的クラスにすることです。

もちろん、この 1 つのメソッドのためだけに 1 つのクラスを作成する必要がある理由を正確に説明するのは困難です。ほとんどの人が想定しているように、それは典型的な「ユーティリティクラス」の状況ですか? または、何らかのルール クラスを実装しており、今後さらに追加される可能性があります。

たとえば、そのクラスをプラグ可能にします。次に、1 つのメソッドのインターフェイスを作成し、すべてのパラメーターをコンストラクターではなくインターフェイスに渡す必要がありますが、静的にしたくないでしょう。

于 2008-10-15T21:12:19.363 に答える
3

このメソッドがステートレスであり、渡す必要がない場合は、静的として定義するのが最も理にかなっています。メソッドを渡す必要がある場合は、他の提案されたアプローチの 1 つではなく、デリゲートの使用を検討してください。

于 2008-10-15T18:58:44.533 に答える
3

単純なアプリケーションとinternalヘルパーには、静的メソッドを使用します。コンポーネントを含むアプリケーションの場合、私はManaged Extensibility Frameworkが大好きです。これは、API 全体で見られるパターンを説明するために書いているドキュメントからの抜粋です。

  • サービス
    • インターフェイスによって定義されI[ServiceName]Serviceます。
    • インターフェイス タイプごとにエクスポートおよびインポートされます。
    • 単一の実装は、ホスト アプリケーションによって提供され、内部および/または拡張機能によって使用されます。
    • サービス インターフェイスのメソッドはスレッド セーフです。

不自然な例として:

public interface ISettingsService
{
    string ReadSetting(string name);

    void WriteSetting(string name, string value);
}

[Export]
public class ObjectRequiringSettings
{
    [Import]
    private ISettingsService SettingsService
    {
        get;
        set;
    }

    private void Foo()
    {
        if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString)
        {
            // whatever
        }
    }
}
于 2010-01-15T18:05:30.353 に答える
3

クラスを静的にすることはできますか?

もしそうなら、私はそれを「ユーティリティ」クラスにして、すべての単一関数クラスを入れます。

于 2008-10-15T17:45:20.420 に答える
2

コンストラクターですべてを行うだけです。そのようです:

new MyClass(arg1, arg2, arg3);// the constructor does everything.

また

MyClass my_object(arg1, arg2, arg3);
于 2008-10-15T18:28:59.693 に答える
0

一挙に事態を回避できるかもしれません。が得られるようにリファクタリングしてみてくださいarg1.myMethod1(arg2, arg3)。より理にかなっている場合は、arg1 を arg2 または arg3 と交換します。

arg1 のクラスを制御できない場合は、次のようにデコレートします。

class Arg1Decorator
    private final T1 arg1;
    public Arg1Decorator(T1 arg1) {
        this.arg1 = arg1;
    }
    public T myMethod(T2 arg2, T3 arg3) {
        ...
    }
 }

 arg1d = new Arg1Decorator(arg1)
 arg1d.myMethod(arg2, arg3)

その理由は、OOP では、データとそのデータを処理するメソッドが一緒に属しているからです。さらに、Mark が言及したすべての利点が得られます。

于 2013-01-31T11:09:15.303 に答える
0

考慮すべきもう1つの重要な問題は、システムがマルチスレッド環境で実行されるかどうか、および静的メソッドまたは変数を持つことがスレッドセーフかどうかです...

システムの状態に注意を払う必要があります。

于 2012-07-27T07:49:40.490 に答える