web-dev-qa-db-ja.com

「Util」クラスが心配の原因ですか?

私は時々、主に実際には他に属していないように見えるメソッドと値を保持するのに役立つ「Util」クラスを作成します。しかし、これらのクラスのいずれかを作成するたびに、どこか悪いところを読んだので、「ええと、後で後悔するつもりです...」と思います。

しかし、その一方で、2つの説得力のある(少なくとも私にとっては)ケースがあるようです。

  1. パッケージ内の複数のクラスで使用される実装の秘密
  2. インターフェイスを乱雑にすることなく、クラスを拡張するための便利な機能を提供する

私は破壊への道を進んでいますか?あなたが言うこと !!リファクタリングする必要がありますか?

14
user39685

現代OO設計は次のことを受け入れませんすべてはオブジェクトです。動作にはいくつかのものがあります。動作や数式であり、状態のないものもあります。これらをモデル化することは良いことです純粋な関数として、そのデザインのメリットを享受できます。

JavaとC#(およびその他)では、utilクラスを作成し、そのフープをジャンプして実行する必要があります。迷惑ですが、世界の終わりではありません。設計の観点からはそれほど面倒ではありません。

26
Telastyn

絶対とは絶対言うな"

私はそれが必ずしも悪いことだとは思いません、あなたがひどくそれを悪用してそれを悪用する場合にのみ悪いのです。

私たちは皆、ツールとユーティリティを必要としています

手始めに、私たちは皆、ほとんどユビキタスである必要があると見なされることがあるいくつかのライブラリを使用しています。たとえば、Java=世界では、 Google Guava または Apache CommonsApache Commons LangApache Commons Collections など) ...)。

したがって、明らかにこれらの必要性があります。

ハードワード、重複、バグの導入を回避する

これらについて考えるなら、あなたが説明するこれらのUtilクラスの非常に大きな束ですが、誰かがそれらを(比較的)正しくするために非常に長い時間をかけて、そして 時間 - テスト済みです そして、他の人によって非常に目を見張られています。

したがって、Utilクラスを作成するのが難しいと感じたときの最初の経験則は、Utilクラスが実際にまだ存在していないことを確認することです。

そのために私が見た唯一の反論は、次の理由で依存関係を制限したい場合です。

  • 依存関係のメモリフットプリントを制限したい場合
  • または、開発者が使用を許可されているものを厳しく制御したい場合(強迫的な大規模なチームで発生するか、特定のフレームワークがどこかで絶対に回避する奇妙なスーパークラッピークラスを持つことがわかっている場合)。

しかし、これらの両方に対処するには、 ProGuard または同等のものを使用してlibを再パッケージ化するか、自分で分解します( Maven ユーザーの場合、 maven-shade-plugin はいくつかの フィルタリングパターン これをビルドの一部として統合します)。

そのため、それがlibにあり、使用例に一致し、それ以外にベンチマークからの指示がない場合は、それを使用してください。それがあなたと少し異なる場合は、それを拡張するか(可能な場合)、拡張するか、最後の手段として再書き込みしてください。

命名規則

ただし、これまでのところ、私はあなたのようにUtilsと呼びました。それらに名前を付けないでください。

それらに意味のある名前を付けます。 Google Guavaを(非常に、非常に)何をすべきかの良い例として取り上げ、com.google.guava名前空間は、実際にはutilルートです。

最悪の場合、パッケージはutilを呼び出しますが、クラスは呼び出しません。 Stringオブジェクトと文字列構造の操作を扱う場合は、 Strings ではなく、 StringUtils と呼びます(申し訳ありませんが、 Apache Commons Lang -私は今でも好きで使用しています!)。特定のことを行う場合は、特定のクラス名を選択します( Splitter または Joiner など)。

単体テスト

これらのユーティリティを作成する必要がある場合は、必ずユニットテストを行ってください。ユーティリティの良い点は、通常、ユーティリティはかなり自己完結型のコンポーネントであり、特定の入力を受け取り、特定の出力を返すことです。それがコンセプトです。したがって、それらを単体テストしないことの言い訳はありません。

また、単体テストでは、APIの契約を定義して文書化できます。テストが失敗した場合は、何かを間違った方法で変更したか、APIのコントラクトを変更しようとしていることを意味します (または、元のテストががらくただった-そこから学び、二度とそれをしないでください)

APIデザイン

これらのAPIについて設計上の決定を下すのは、おそらく長い間続くでしょう。したがって、Splitter- cloneの作成に何時間も費やさないようにしながら、問題への取り組み方に注意してください。

いくつか質問してみてください。

  • ユーティリティメソッドは、それ自体でクラスを保証しますか、それとも、同じように有用なメソッドのグループの一部であることが理にかなっている場合、静的メソッドで十分ですか?
  • オブジェクトを作成してAPIを読みやすくするには、 ファクトリメソッド が必要ですか。
  • 読みやすさといえば、 Fluent APIビルダー などが必要ですか?

これらのutilsは、広範囲のユースケースをカバーし、堅牢で安定しており、十分に文書化されており、驚きを最小限に抑えるという原則に従い、自己完結型にする必要があります。理想的には、utilsの各サブパッケージ、または少なくともutilパッケージ全体を、簡単に再利用できるようにバンドルにエクスポートできる必要があります。

いつものように、ここで巨人から学びます:

はい、これらの多くはコレクションとデータ構造に重点を置いていますが、直接または間接的にほとんどのユーティリティを実装する可能性が高い場所や内容ではないことを教えてください。

16
haylem

Utilクラスはまとまりがなく、クラスは変更する単一の理由(単一の責任の原則)を持たなければならないので、一般的には悪い設計です。

それでも、Java API、MathCollectionsArraysなどの非常に「util」クラスを見てきました。

これらは実際にはユーティリティクラスですが、すべてのメソッドは1つのテーマに関連しています。1つは数学演算、もう1つはコレクションを操作するメソッド、もう1つは配列を操作するメソッドです。

ユーティリティクラスにまったく関連のないメソッドがないようにしてください。その場合、それらが実際に属している場所に他の場所に置くこともできます。

Utilクラスが必要な場合は、JavaのMathCollectionsArraysのようにテーマで区切るように試みますです。少なくとも名前空間である場合でも、少なくとも設計の意図は示されています。

私は1つとして、常にユーティリティクラスを避け、作成するためにneedを使用したことがありません。

8

tilクラスを使用することは完全に許容されますが、私はClassNameHelperという用語を好みます。 .NET BCLには、ヘルパークラスも含まれています。覚えておくべき最も重要なことは、クラスの目的と個々のヘルパーメソッドを徹底的に文書化し、高品質で保守可能なコードにすることです。

ヘルパークラスに夢中にならないでください。

3
David Anderson

私は2段階のアプローチを使用しています。 「util」パッケージ(フォルダ)内の「Globals」クラス。 「Globals」クラスまたは「util」パッケージに入れるものは、次のようにする必要があります。

  1. シンプル、
  2. 変わらない、
  3. アプリケーションの他のものに依存しない

これらのテストに合格する例:

  • システム全体の日付と10進数の形式
  • EARLIEST_YEAR(システムがサポートする)やIS_TEST_MODEなどの不変のグローバルデータ、またはそのような真にグローバルで変化しない(システムの実行中)値。
  • 言語、フレームワーク、またはツールキットのギャップを回避する非常に小さなヘルパーメソッド

以下は、アプリケーションの他の部分から完全に独立した、非常に小さなヘルパーメソッドの例です。

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パッケージに貼り付けたすべてのものに最適な場所がない理由を正当化でき、上記のテストを使用して定期的にクリーンアップする必要があります。それ以外の場合は、単に混乱を引き起こしています。

0
GlenPeterson