1. ホーム
  2. c#

[解決済み] 単一のメソッドを持つクラス -- 最適なアプローチ?

2022-04-20 23:39:15

質問

一つの関数を実行するためのクラスがあるとします。関数を実行した後、そのクラスは破棄することができます。これらのアプローチのうち、どちらかを選ぶ理由はあるのでしょうか?

// 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つの(パブリック)メソッドしか必要ないものなのです。

どのように解決するのか?

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

ポリモーフィズム

例えば、UtilityClass.SomeMethodというメソッドがあり、楽しげに動作しているとします。突然、その機能を少し変更する必要が出てきました。ほとんどの機能は同じですが、それでもいくつかの部分を変更する必要があります。もしこれが静的メソッドでなかったら、派生クラスを作って必要に応じてメソッドの内容を変更することができます。スタティック・メソッドである以上、それはできない。確かに、古いメソッドの前後に機能を追加するだけなら、新しいクラスを作成して、その中で古いメソッドを呼び出せばいいのですが、それは単に気持ち悪いだけです。

インターフェイスの悩み

静的メソッドは、論理的な理由からインターフェースを通して定義することができません。また、スタティックメソッドをオーバーライドできないので、スタティッククラスをインターフェースで受け渡しする必要がある場合、スタティッククラスは役に立ちません。このため、strategyパターンの一部として静的クラスを使用することはできません。この問題を解決するために インターフェースの代わりにデリゲートを渡す .

テスト

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

ブロブ(塊)の育成

静的メソッドは通常ユーティリティメソッドとして使われ、ユーティリティメソッドは通常異なる目的を持つので、すぐにまとまりのない機能でいっぱいの大きなクラスになってしまいます。理想的には、各クラスはシステムの中で単一の目的を持つべきです。目的がきちんと定義されているのであれば、むしろ5倍くらいのクラスがあってもいいと思います。

パラメータクリープ

最初は、その小さなかわいい、無邪気な静的メソッドが1つのパラメータを取るかもしれません。機能が大きくなるにつれて、いくつかの新しいパラメータが追加されます。やがて、さらにオプションのパラメータが追加されるので、メソッドのオーバーロードを作成します(または、サポートしている言語ではデフォルト値を追加するだけです)。やがて、10個のパラメータを受け取るメソッドができあがります。本当に必要なのは最初の3つだけで、4から7はオプションである。しかし、パラメータ6を指定すると、7-9も入力する必要がある...。この静的メソッドと同じことをするためだけにクラスを作るのであれば、コンストラクタで必要なパラメータを取り込み、ユーザーがプロパティやメソッドで任意の値を設定し、相互に依存する複数の値を同時に設定できるようにすれば解決するはずです。また、メソッドがこれほど複雑になってしまった場合、どうせなら独自のクラスにする必要がある場合がほとんどです。

理由もなくクラスのインスタンスを作成するようコンシューマに要求する。

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

シスだけが絶対的なものを扱える

もちろん、私がスタティック・メソッドを嫌うのには例外があります。肥大化の心配のない真のユーティリティクラスは、静的メソッドの優れたケースです。例えば、System.Convert。もし、あなたのプロジェクトが、将来のメンテナンスの必要性のない一過性のものであれば、全体のアーキテクチャはあまり重要ではありません。

標準、標準、標準!

インスタンスメソッドを使うことは、スタティックメソッドを使うことを阻害するものではありませんし、その逆もまた然りです。差別化の背後に理由があり、それが標準化されている限りは。ビジネスレイヤーに様々な実装方法が散らばっているのを見るのは、何にも勝る苦痛です。