私は時々、主に実際には他に属していないように見えるメソッドと値を保持するのに役立つ「Util」クラスを作成します。しかし、これらのクラスのいずれかを作成するたびに、どこか悪いところを読んだので、「ええと、後で後悔するつもりです...」と思います。
しかし、その一方で、2つの説得力のある(少なくとも私にとっては)ケースがあるようです。
私は破壊への道を進んでいますか?あなたが言うこと !!リファクタリングする必要がありますか?
現代OO設計は次のことを受け入れませんすべてはオブジェクトです。動作にはいくつかのものがあります。動作や数式であり、状態のないものもあります。これらをモデル化することは良いことです純粋な関数として、そのデザインのメリットを享受できます。
JavaとC#(およびその他)では、utilクラスを作成し、そのフープをジャンプして実行する必要があります。迷惑ですが、世界の終わりではありません。設計の観点からはそれほど面倒ではありません。
私はそれが必ずしも悪いことだとは思いません、あなたがひどくそれを悪用してそれを悪用する場合にのみ悪いのです。
手始めに、私たちは皆、ほとんどユビキタスである必要があると見なされることがあるいくつかのライブラリを使用しています。たとえば、Java=世界では、 Google Guava または Apache Commons ( Apache Commons Lang 、 Apache Commons Collections など) ...)。
したがって、明らかにこれらの必要性があります。
これらについて考えるなら、あなたが説明するこれらのUtil
クラスの非常に大きな束ですが、誰かがそれらを(比較的)正しくするために非常に長い時間をかけて、そして 時間 - テスト済みです そして、他の人によって非常に目を見張られています。
したがって、Util
クラスを作成するのが難しいと感じたときの最初の経験則は、Util
クラスが実際にまだ存在していないことを確認することです。
そのために私が見た唯一の反論は、次の理由で依存関係を制限したい場合です。
しかし、これらの両方に対処するには、 ProGuard または同等のものを使用してlibを再パッケージ化するか、自分で分解します( Maven ユーザーの場合、 maven-shade-plugin はいくつかの フィルタリングパターン これをビルドの一部として統合します)。
そのため、それがlibにあり、使用例に一致し、それ以外にベンチマークからの指示がない場合は、それを使用してください。それがあなたと少し異なる場合は、それを拡張するか(可能な場合)、拡張するか、最後の手段として再書き込みしてください。
ただし、これまでのところ、私はあなたのようにUtil
sと呼びました。それらに名前を付けないでください。
それらに意味のある名前を付けます。 Google Guavaを(非常に、非常に)何をすべきかの良い例として取り上げ、com.google.guava
名前空間は、実際にはutil
ルートです。
最悪の場合、パッケージはutil
を呼び出しますが、クラスは呼び出しません。 String
オブジェクトと文字列構造の操作を扱う場合は、 Strings
ではなく、 StringUtils
と呼びます(申し訳ありませんが、 Apache Commons Lang -私は今でも好きで使用しています!)。特定のことを行う場合は、特定のクラス名を選択します( Splitter
または Joiner
など)。
これらのユーティリティを作成する必要がある場合は、必ずユニットテストを行ってください。ユーティリティの良い点は、通常、ユーティリティはかなり自己完結型のコンポーネントであり、特定の入力を受け取り、特定の出力を返すことです。それがコンセプトです。したがって、それらを単体テストしないことの言い訳はありません。
また、単体テストでは、APIの契約を定義して文書化できます。テストが失敗した場合は、何かを間違った方法で変更したか、APIのコントラクトを変更しようとしていることを意味します (または、元のテストががらくただった-そこから学び、二度とそれをしないでください)。
これらのAPIについて設計上の決定を下すのは、おそらく長い間続くでしょう。したがって、Splitter
- cloneの作成に何時間も費やさないようにしながら、問題への取り組み方に注意してください。
いくつか質問してみてください。
これらのutilsは、広範囲のユースケースをカバーし、堅牢で安定しており、十分に文書化されており、驚きを最小限に抑えるという原則に従い、自己完結型にする必要があります。理想的には、utilsの各サブパッケージ、または少なくともutilパッケージ全体を、簡単に再利用できるようにバンドルにエクスポートできる必要があります。
いつものように、ここで巨人から学びます:
はい、これらの多くはコレクションとデータ構造に重点を置いていますが、直接または間接的にほとんどのユーティリティを実装する可能性が高い場所や内容ではないことを教えてください。
Utilクラスはまとまりがなく、クラスは変更する単一の理由(単一の責任の原則)を持たなければならないので、一般的には悪い設計です。
それでも、Java API、Math
、Collections
、Arrays
などの非常に「util」クラスを見てきました。
これらは実際にはユーティリティクラスですが、すべてのメソッドは1つのテーマに関連しています。1つは数学演算、もう1つはコレクションを操作するメソッド、もう1つは配列を操作するメソッドです。
ユーティリティクラスにまったく関連のないメソッドがないようにしてください。その場合、それらが実際に属している場所に他の場所に置くこともできます。
Utilクラスが必要な場合は、JavaのMath
、Collections
、Arrays
のようにテーマで区切るように試みますです。少なくとも名前空間である場合でも、少なくとも設計の意図は示されています。
私は1つとして、常にユーティリティクラスを避け、作成するためにneedを使用したことがありません。
tilクラスを使用することは完全に許容されますが、私はClassNameHelper
という用語を好みます。 .NET BCLには、ヘルパークラスも含まれています。覚えておくべき最も重要なことは、クラスの目的と個々のヘルパーメソッドを徹底的に文書化し、高品質で保守可能なコードにすることです。
ヘルパークラスに夢中にならないでください。
私は2段階のアプローチを使用しています。 「util」パッケージ(フォルダ)内の「Globals」クラス。 「Globals」クラスまたは「util」パッケージに入れるものは、次のようにする必要があります。
これらのテストに合格する例:
以下は、アプリケーションの他の部分から完全に独立した、非常に小さなヘルパーメソッドの例です。
public static String ordinal(final int i) {
return (i == 1) ? "1st" :
(i == 2) ? "2nd" :
(i == 3) ? "3rd" :
new StringBuilder().append(i).append("th").toString();
}
これを見ると、21は「21番」、22は「22番」などのバグを確認できますが、それは要点の外です。
これらのヘルパーメソッドの1つが大きくなったり複雑になったりした場合は、utilパッケージの独自のクラスに移動する必要があります。 utilパッケージ内の2つ以上のヘルパークラスが互いに関連している場合、それらを独自のパッケージに移動する必要があります。定数またはヘルパーメソッドがアプリケーションの特定の部分に関連していることが判明した場合は、そこに移動する必要があります。
Globalsクラスまたはutilパッケージに貼り付けたすべてのものに最適な場所がない理由を正当化でき、上記のテストを使用して定期的にクリーンアップする必要があります。それ以外の場合は、単に混乱を引き起こしています。