web-dev-qa-db-ja.com

単一のメソッドを持つクラス-最良のアプローチ?

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

// 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つの(パブリック)メソッドだけです。

163
JW.

以前は、静的メソッドで満たされたユーティリティクラスが大好きでした。彼らは、そうでなければ冗長性と保守の地獄を引き起こすことを避けなければならないであろうヘルパーメソッドを大きく統合しました。それらは非常に使いやすく、インスタンス化も廃棄もされず、ただ忘れるだけです。これは、サービス指向アーキテクチャを作成するための最初の無意識の試みでした-ちょうど仕事をしただけで他には何もない多くのステートレスなサービスだと思います。しかし、システムが成長するにつれて、ドラゴンが登場します。

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

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

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

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

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

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

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

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

256

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

メソッドに対してのみ存在するクラスは静的なままにしておく必要があります。

86
jjnguy

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

17
Ray Jezek

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

また、無関係な静的メソッドが多数ある巨大なUtilsクラスに対しても警告します。これは急いで混乱して扱いにくくなる可能性があります。多くのクラスがあり、それぞれに関連するメソッドが少ない方が良いでしょう。

15
Bill the Lizard

静的メソッド形式がより良いオプションだと思います。また、クラスを静的にすることで、クラスのインスタンスを誤って作成することを心配する必要がなくなります。

6
Nathen Silver

私はここで状況が本当にわかりませんが、arg1、arg2またはarg3が属するクラスの1つにメソッドとして置くことを検討します-それらのクラスの1つが所有していると意味的に言えば方法。

5
Chris Cudmore

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

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

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

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

4
Nick

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

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

3
Robert

単純なアプリケーションと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
        }
    }
}
3
Sam Harwell

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

3
Joel Wietelmann

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

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

または

MyClass my_object(arg1, arg2, arg3);
2
yigal

クラスまたはクラスのインスタンスのプロパティがコンストラクターまたはメソッドで使用されない場合、メソッドは「静的」パターンのように設計することは推奨されません。静的メソッドは常に「ヘルプ」のように考えるべきです。

0
user7545070

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

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

0
NZal

状況をすべて一緒に回避できる場合があります。リファクタリングして、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では、データとそのデータを処理するメソッドが一緒に属するということです。さらに、マークが言及したすべての利点が得られます。

0
Hugo Wood