回答:
一般的な静的メソッドには2種類あります。
シングルトンパターンなど、「安全でない」静的要素の一般的な使用方法はいくつかありますが、それらを呼び出すきれいな名前があっても、グローバル変数を変更するだけであることに注意してください。安全でない静力学を使用する前に注意深く考えてください。
内部状態のないオブジェクトは疑わしいものです。
通常、オブジェクトは状態と動作をカプセル化します。動作のみをカプセル化するオブジェクトは奇妙です。場合によっては、LightweightまたはFlyweightの例です。
それ以外の場合は、オブジェクト言語で行われる手続き型の設計です。
これは、実際にはJohn Millikinのすばらしい回答のフォローアップにすぎません。
ステートレスメソッド(ほとんどの関数)を静的にしても安全ですが、変更が難しい結合が発生する場合があります。次のような静的メソッドがあるとします。
public class StaticClassVersionOne {
public static void doSomeFunkyThing(int arg);
}
あなたは次のように呼びます:
StaticClassVersionOne.doSomeFunkyThing(42);
これは、静的メソッドの動作を変更しなければならない場合に遭遇し、にしっかりと拘束されていることに気付くまでは、すべてうまくいき、非常に便利ですStaticClassVersionOne
。おそらくコードを変更して問題はないかもしれませんが、古い動作に依存している他の呼び出し元が存在する場合は、メソッドの本体で説明する必要があります。場合によっては、メソッド本体がこれらのすべての動作のバランスをとろうとすると、メソッド本体がかなり醜くなったり、保守不能になったりすることがあります。メソッドを分割する場合は、コードをいくつかの場所で変更して、それを考慮するか、新しいクラスを呼び出す必要がある場合があります。
しかし、メソッドを提供するインターフェースを作成し、それを呼び出し元に渡した場合、動作を変更する必要がある場合は、新しいクラスを作成してインターフェースを実装できます。これにより、クリーンでテストが容易になり、保守性が向上します。それは代わりに呼び出し元に与えられます。このシナリオでは、呼び出し元のクラスを変更したり再コンパイルしたりする必要はなく、変更はローカライズされています。
それはありそうな状況かもしれませんし、そうでないかもしれませんが、私は検討する価値があると思います。
もう1つのオプションは、元のオブジェクトの非静的メソッドとしてそれらを追加することです。
つまり、変更:
public class BarUtil {
public static Foo transform(Bar toFoo) { ... }
}
に
public class Bar {
...
public Foo transform() { ...}
}
ただし、多くの状況ではこれは不可能です(たとえば、XSD / WSDLなどからの通常のクラスコード生成)。または、クラスが非常に長くなり、変換メソッドは、多くの場合、複雑なオブジェクトにとって本当に苦痛であり、単にそれらを必要とするだけです。独自のクラスで。ええ、私はユーティリティクラスに静的メソッドを持っています。
静的クラスは、適切な場所で使用されている限り問題ありません。
つまり、「リーフ」メソッドであるメソッド(状態を変更せず、入力を何らかの形で変換するだけです)。この良い例は、Path.Combineのようなものです。この種のものは有用であり、構文を簡潔にするのに役立ちます。
問題私は静的で持っては数多くあります。
まず、静的クラスがある場合、依存関係は非表示になります。以下を検討してください。
public static class ResourceLoader
{
public static void Init(string _rootPath) { ... etc. }
public static void GetResource(string _resourceName) { ... etc. }
public static void Quit() { ... etc. }
}
public static class TextureManager
{
private static Dictionary<string, Texture> m_textures;
public static Init(IEnumerable<GraphicsFormat> _formats)
{
m_textures = new Dictionary<string, Texture>();
foreach(var graphicsFormat in _formats)
{
// do something to create loading classes for all
// supported formats or some other contrived example!
}
}
public static Texture GetTexture(string _path)
{
if(m_textures.ContainsKey(_path))
return m_textures[_path];
// How do we know that ResourceLoader is valid at this point?
var texture = ResourceLoader.LoadResource(_path);
m_textures.Add(_path, texture);
return texture;
}
public static Quit() { ... cleanup code }
}
TextureManagerを見ると、コンストラクターを見て、どの初期化手順を実行する必要があるのかわかりません。クラスの詳細を調べて、その依存関係を見つけ、正しい順序で初期化する必要があります。この場合、実行前にResourceLoaderを初期化する必要があります。今、この依存関係の悪夢を拡大し、おそらく何が起こるかを推測できます。初期化の明示的な順序がない場所でコードを維持しようとしていると想像してください。これをインスタンスの依存性注入と比較してください。その場合、依存関係が満たされない場合、コードはコンパイルされません。
さらに、状態を変更する静力学を使用する場合、それはカードの家のようなものです。誰が何にアクセスできるかは決してわかりません。デザインはスパゲッティモンスターに似ている傾向があります。
最後に、そして同じくらい重要なことですが、静的を使用すると、プログラムが特定の実装に結び付けられます。静的コードは、テスト容易性のための設計の正反対です。静的な問題に悩まされているコードのテストは悪夢です。静的呼び出しは、(静的型を模擬するように特別に設計されたテストフレームワークを使用しない限り)テストdoubleと入れ替えることはできません。そのため、静的システムは、それを使用するすべてを即時統合テストにします。
簡単に言えば、静的なものは問題のないものもあれば、小さなツールや使い捨てのコードの場合は使用しないでください。ただし、それを超えると、保守性、優れた設計、およびテストの容易さにとって、悪夢のような悪夢になります。
ここに問題に関する良い記事があります:http : //gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/
それは合理的なアプローチのようです。あまり多くの静的クラス/メソッドを使用したくない理由は、オブジェクト指向プログラミングから、構造化プログラミングの領域へと移行してしまうためです。
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に賛成です。
多くの静的メソッドとシングルトンを持つクラスの間を行ったり来たりしていました。どちらも問題を解決しますが、シングルトンは簡単に複数に置き換えることができます。(プログラマーは常に1つしかないと確信しているようであり、非常に限られた場合を除いて、静的メソッドを完全に放棄するのに十分なほど間違っていることに気付きました)。
とにかく、シングルトンを使用すると、後で何かをファクトリに渡して別のインスタンスを取得することができ、リファクタリングなしでプログラム全体の動作が変更されます。静的メソッドのグローバルクラスを別の「バッキング」データまたは動作がわずかに異なるもの(子クラス)に変更することは、お尻の大きな痛みです。
また、静的メソッドには同様の利点はありません。
そう、彼らは悪いです。
ユーティリティメソッドの場合は、静的にすると便利です。GuavaとApache Commonsはこの原則に基づいて構築されています。
これに対する私の意見は純粋に実用的です。それがアプリのコードである場合、静的メソッドは通常、最善の方法ではありません。静的メソッドには、ユニットテストに重大な制限があります。簡単にモックすることはできません。モックした静的機能を他のテストに挿入することはできません。通常、機能を静的メソッドに挿入することもできません。
したがって、私のアプリロジックには、通常、静的なユーティリティのような小さなメソッド呼び出しがあります。すなわち
static cutNotNull(String s, int length){
return s == null ? null : s.substring(0, length);
}
利点の1つは、そのような方法をテストしないことです:-)
もちろん、特効薬はありません。静的クラスは、小さなユーティリティ/ヘルパーには問題ありません。しかし、ビジネスロジックプログラミングに静的メソッドを使用することは確かに悪です。次のコードを検討してください
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);
It への静的メソッド呼び出しが発生する
BusinessService
分離からテストすることはできませんItemsProcessor
(ほとんどのテストツールは静的クラスをモックしません)。これにより、単体テストが不可能になります。単体テストなし==低品質